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

14,243 lines 540.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 e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
2 'use strict';
3
4 Object.defineProperty(exports, "__esModule", {
5 value: true
6 });
7 function init() {
8 document.body.addEventListener('click', handleClickEvent, true);
9 }
10
11 function handleClickEvent(e) {
12 if (e.target.tagName !== 'A') {
13 return;
14 }
15
16 if (e.target.hasAttribute('data-hf-confirm')) {
17 var sure = confirm(e.target.getAttribute('data-hf-confirm'));
18
19 if (!sure) {
20 e.preventDefault();
21 }
22 }
23 }
24
25 exports.default = {
26 init: init
27 };
28
29 },{}],2:[function(require,module,exports){
30 'use strict';
31
32 var _tabs = require('./tabs.js');
33
34 var _tabs2 = _interopRequireDefault(_tabs);
35
36 var _formEditor = require('./form-editor.js');
37
38 var _formEditor2 = _interopRequireDefault(_formEditor);
39
40 var _formActions = require('./form-actions.js');
41
42 var _formActions2 = _interopRequireDefault(_formActions);
43
44 var _fieldBuilder = require('./field-builder.js');
45
46 var _fieldBuilder2 = _interopRequireDefault(_fieldBuilder);
47
48 var _actionConfirmations = require('./action-confirmations.js');
49
50 var _actionConfirmations2 = _interopRequireDefault(_actionConfirmations);
51
52 var _tlite = require('tlite');
53
54 var _tlite2 = _interopRequireDefault(_tlite);
55
56 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
57
58 // init the various components
59 _tabs2.default.init();
60 _actionConfirmations2.default.init();
61
62 if (document.getElementById('hf-form-editor')) {
63 _formEditor2.default.init();
64 _formActions2.default.init();
65 _fieldBuilder2.default.init(_formEditor2.default);
66 }
67
68 (0, _tlite2.default)(function (el) {
69 return el.className.indexOf('hf-tooltip') > -1;
70 });
71
72 // expose variables
73 window.html_forms = {
74 'FieldBuilder': _fieldBuilder2.default,
75 'Editor': _formEditor2.default
76 };
77
78 },{"./action-confirmations.js":1,"./field-builder.js":3,"./form-actions.js":6,"./form-editor.js":7,"./tabs.js":8,"tlite":21}],3:[function(require,module,exports){
79 'use strict';
80
81 Object.defineProperty(exports, "__esModule", {
82 value: true
83 });
84
85 var _createClass = function () { 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
86
87 var _desc, _value, _class, _desc2, _value2, _class2;
88
89 var _preact = require('preact');
90
91 var _linkstate = require('linkstate');
92
93 var _linkstate2 = _interopRequireDefault(_linkstate);
94
95 var _configFields = require('./field-builder/config-fields.js');
96
97 var _html = require('./field-builder/html.js');
98
99 var _decko = require('decko');
100
101 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
102
103 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
104
105 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
106
107 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
108
109 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
110 var desc = {};
111 Object['ke' + 'ys'](descriptor).forEach(function (key) {
112 desc[key] = descriptor[key];
113 });
114 desc.enumerable = !!desc.enumerable;
115 desc.configurable = !!desc.configurable;
116
117 if ('value' in desc || desc.initializer) {
118 desc.writable = true;
119 }
120
121 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
122 return decorator(target, property, desc) || desc;
123 }, desc);
124
125 if (context && desc.initializer !== void 0) {
126 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
127 desc.initializer = undefined;
128 }
129
130 if (desc.initializer === void 0) {
131 Object['define' + 'Property'](target, property, desc);
132 desc = null;
133 }
134
135 return desc;
136 }
137
138 var Editor = void 0;
139
140
141 var fields = {
142 "text": "Text",
143 "email": "Email",
144 "url": "URL",
145 "number": "Number",
146 "date": "Date",
147 "textarea": "Textarea",
148 "dropdown": "Dropdown",
149 "checkboxes": "Checkboxes",
150 "radio-buttons": "Radio buttons",
151 "submit": "Submit button"
152 };
153
154 var FieldBuilder = (_class = function (_Component) {
155 _inherits(FieldBuilder, _Component);
156
157 function FieldBuilder() {
158 _classCallCheck(this, FieldBuilder);
159
160 var _this = _possibleConstructorReturn(this, (FieldBuilder.__proto__ || Object.getPrototypeOf(FieldBuilder)).call(this));
161
162 _this.state = {
163 fieldType: ""
164 };
165 return _this;
166 }
167
168 _createClass(FieldBuilder, [{
169 key: 'handleCancel',
170 value: function handleCancel() {
171 this.setState({
172 fieldType: ""
173 });
174 }
175 }, {
176 key: 'openFieldConfig',
177 value: function openFieldConfig(e) {
178 var newFieldType = e.target.value;
179
180 if (this.state.fieldType === newFieldType) {
181 this.setState({ fieldType: "" });
182 } else {
183 this.setState({ fieldType: newFieldType });
184 }
185 }
186 }, {
187 key: 'render',
188 value: function render(props, state) {
189 var _this2 = this;
190
191 var fieldButtons = Object.keys(fields).map(function (key) {
192 var label = fields[key];
193 return (0, _preact.h)(
194 'button',
195 { type: 'button', value: key, className: "button " + (state.fieldType === key ? "active" : ""), onClick: _this2.openFieldConfig },
196 label
197 );
198 });
199
200 return (0, _preact.h)(
201 'div',
202 { 'class': 'hf-field-builder' },
203 (0, _preact.h)(
204 'h4',
205 null,
206 'Add field'
207 ),
208 (0, _preact.h)(
209 'div',
210 { 'class': 'available-fields' },
211 fieldButtons
212 ),
213 (0, _preact.h)(
214 'div',
215 { style: 'max-width: 480px;' },
216 (0, _preact.h)(FieldConfigurator, { fieldType: state.fieldType, onCancel: this.handleCancel })
217 ),
218 state.fieldType === "" ? (0, _preact.h)(
219 'p',
220 { 'class': 'help', style: 'margin-bottom: 0;' },
221 'Use the buttons above to generate your field HTML, or manually modify your form below.'
222 ) : ""
223 );
224 }
225 }]);
226
227 return FieldBuilder;
228 }(_preact.Component), (_applyDecoratedDescriptor(_class.prototype, 'handleCancel', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'handleCancel'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'openFieldConfig', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'openFieldConfig'), _class.prototype)), _class);
229 var FieldConfigurator = (_class2 = function (_Component2) {
230 _inherits(FieldConfigurator, _Component2);
231
232 function FieldConfigurator(props) {
233 _classCallCheck(this, FieldConfigurator);
234
235 var _this3 = _possibleConstructorReturn(this, (FieldConfigurator.__proto__ || Object.getPrototypeOf(FieldConfigurator)).call(this, props));
236
237 _this3.state = _this3.getInitialState();
238 _this3.choiceHandlers = {
239 "add": _this3.addChoice,
240 "delete": _this3.deleteChoice,
241 "changeLabel": _this3.changeChoiceLabel,
242 "toggleChecked": _this3.toggleChoiceChecked
243 };
244 return _this3;
245 }
246
247 _createClass(FieldConfigurator, [{
248 key: 'getInitialState',
249 value: function getInitialState() {
250 return {
251 fieldType: this.props.fieldType,
252 fieldLabel: "",
253 placeholder: "",
254 value: "",
255 wrap: true,
256 required: false,
257 choices: [{
258 checked: false,
259 label: "One"
260 }, {
261 checked: false,
262 label: "Two"
263 }]
264 };
265 }
266 }, {
267 key: 'componentWillReceiveProps',
268 value: function componentWillReceiveProps(props) {
269 this.setState({
270 fieldType: props.fieldType
271 });
272 }
273 }, {
274 key: 'addToForm',
275 value: function addToForm() {
276 var html = (0, _html.htmlgenerate)(this.state);
277 Editor.replaceSelection(html);
278 }
279 }, {
280 key: 'addChoice',
281 value: function addChoice() {
282 var arr = this.state.choices;
283 arr.push({ checked: false, label: "..." });
284 this.setState({ choices: arr });
285 }
286 }, {
287 key: 'deleteChoice',
288 value: function deleteChoice(e) {
289 var arr = this.state.choices;
290 var index = e.target.parentElement.getAttribute('data-key');
291 arr.splice(index, 1);
292 this.setState({ choices: arr });
293 }
294 }, {
295 key: 'changeChoiceLabel',
296 value: function changeChoiceLabel(e) {
297 var arr = this.state.choices;
298 var index = e.target.parentElement.getAttribute('data-key');
299 arr[index].label = e.target.value;
300 this.setState({ choices: arr });
301 }
302 }, {
303 key: 'toggleChoiceChecked',
304 value: function toggleChoiceChecked(e) {
305 var arr = this.state.choices;
306 var index = e.target.parentElement.getAttribute('data-key');
307 arr[index].checked = !arr[index].checked;
308 this.setState({ choices: arr });
309 }
310 }, {
311 key: 'handleCancel',
312 value: function handleCancel() {
313 // revert back to initial state
314 this.setState(this.getInitialState());
315 this.props.onCancel();
316 }
317 }, {
318 key: 'render',
319 value: function render(props, state) {
320 if (state.fieldType === "") {
321 return "";
322 }
323
324 var formFields = void 0;
325
326 switch (state.fieldType) {
327 case "text":
328 case "email":
329 case "url":
330 case "number":
331 case "textarea":
332 formFields = (0, _preact.h)(
333 'div',
334 null,
335 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
336 (0, _preact.h)(_configFields.Placeholder, { value: state.placeholder, onChange: (0, _linkstate2.default)(this, 'placeholder') }),
337 (0, _preact.h)(_configFields.DefaultValue, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }),
338 (0, _preact.h)(_configFields.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }),
339 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
340 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
341 );
342 break;
343 case "submit":
344 formFields = (0, _preact.h)(
345 'div',
346 null,
347 (0, _preact.h)(_configFields.ButtonText, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }),
348 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
349 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
350 );
351 break;
352
353 case "date":
354 formFields = (0, _preact.h)(
355 'div',
356 null,
357 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
358 (0, _preact.h)(_configFields.DefaultValue, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }),
359 (0, _preact.h)(_configFields.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }),
360 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
361 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
362 );
363 break;
364 case "dropdown":
365 formFields = (0, _preact.h)(
366 'div',
367 null,
368 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
369 (0, _preact.h)(_configFields.Choices, { multiple: false, choices: state.choices, handlers: this.choiceHandlers }),
370 (0, _preact.h)(_configFields.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }),
371 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
372 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
373 );
374 break;
375
376 case "radio-buttons":
377 case "checkboxes":
378 formFields = (0, _preact.h)(
379 'div',
380 null,
381 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
382 (0, _preact.h)(_configFields.Choices, { multiple: state.fieldType === "checkboxes", choices: state.choices, handlers: this.choiceHandlers }),
383 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
384 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
385 );
386 break;
387 }
388
389 return (0, _preact.h)(
390 'div',
391 { 'class': 'field-config', onKeyPress: FieldConfigurator.handleKeyPress },
392 formFields
393 );
394 }
395 }], [{
396 key: 'handleKeyPress',
397 value: function handleKeyPress(e) {
398 // stop RETURN from submitting the parent form.
399 if (e.keyCode === 13) {
400 e.preventDefault();
401 }
402 }
403 }]);
404
405 return FieldConfigurator;
406 }(_preact.Component), (_applyDecoratedDescriptor(_class2.prototype, 'addToForm', [_decko.bind], Object.getOwnPropertyDescriptor(_class2.prototype, 'addToForm'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'addChoice', [_decko.bind], Object.getOwnPropertyDescriptor(_class2.prototype, 'addChoice'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'deleteChoice', [_decko.bind], Object.getOwnPropertyDescriptor(_class2.prototype, 'deleteChoice'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'changeChoiceLabel', [_decko.bind], Object.getOwnPropertyDescriptor(_class2.prototype, 'changeChoiceLabel'), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, 'toggleChoiceChecked', [_decko.bind], Object.getOwnPropertyDescriptor(_class2.prototype, 'toggleChoiceChecked'), _class2.prototype), _applyDecoratedDescriptor(_class2, 'handleKeyPress', [_decko.bind], Object.getOwnPropertyDescriptor(_class2, 'handleKeyPress'), _class2), _applyDecoratedDescriptor(_class2.prototype, 'handleCancel', [_decko.bind], Object.getOwnPropertyDescriptor(_class2.prototype, 'handleCancel'), _class2.prototype)), _class2);
407 exports.default = {
408 init: function init(editor) {
409 Editor = editor;
410
411 (0, _preact.render)((0, _preact.h)(FieldBuilder, null), document.getElementById('hf-field-builder'));
412 }
413 };
414
415 },{"./field-builder/config-fields.js":4,"./field-builder/html.js":5,"decko":17,"linkstate":18,"preact":20}],4:[function(require,module,exports){
416 "use strict";
417
418 Object.defineProperty(exports, "__esModule", {
419 value: true
420 });
421 exports.ButtonText = exports.Choices = exports.Required = exports.Wrap = exports.DefaultValue = exports.Placeholder = exports.Label = exports.AddToForm = undefined;
422
423 var _preact = require("preact");
424
425 function AddToForm(props) {
426 return (0, _preact.h)(
427 "div",
428 { "class": "hf-small-margin" },
429 (0, _preact.h)(
430 "button",
431 { "class": "button", type: "button", onClick: props.onSubmit },
432 "Add field to form"
433 ),
434 " \xA0 ",
435 (0, _preact.h)(
436 "a",
437 { href: "javascript:void(0);", "class": "hf-small", style: "vertical-align: middle;", onClick: props.onCancel },
438 "or close field helper"
439 )
440 );
441 }
442
443 function Label(props) {
444 return (0, _preact.h)(
445 "div",
446 { "class": "hf-small-margin" },
447 (0, _preact.h)(
448 "label",
449 { "for": "hf-fg-field-label" },
450 "Field label ",
451 (0, _preact.h)(
452 "span",
453 { "class": "hf-required" },
454 "*"
455 )
456 ),
457 (0, _preact.h)("input", { id: "hf-fg-field-label", type: "text", value: props.value, onChange: props.onChange })
458 );
459 }
460
461 function Placeholder(props) {
462 return (0, _preact.h)(
463 "div",
464 { "class": "hf-small-margin" },
465 (0, _preact.h)(
466 "label",
467 { "for": "hf-fg-placeholder" },
468 "Placeholder ",
469 (0, _preact.h)(
470 "span",
471 { "class": "hf-italic hf-pull-right" },
472 "Optional"
473 )
474 ),
475 (0, _preact.h)("input", { id: "hf-fg-placeholder", type: "text", value: props.value, onChange: props.onChange }),
476 (0, _preact.h)(
477 "p",
478 { "class": "help" },
479 "Text to show when field has no value."
480 )
481 );
482 }
483
484 function ButtonText(props) {
485 return (0, _preact.h)(
486 "div",
487 { "class": "hf-small-margin" },
488 (0, _preact.h)(
489 "label",
490 { "for": "hf-fg-default-value" },
491 "Button text ",
492 (0, _preact.h)(
493 "span",
494 { "class": "hf-required" },
495 "*"
496 )
497 ),
498 (0, _preact.h)("input", { id: "hf-fg-default-value", type: "text", value: props.value, onChange: props.onChange }),
499 (0, _preact.h)(
500 "p",
501 { "class": "help" },
502 "Text to show on the button."
503 )
504 );
505 }
506
507 function DefaultValue(props) {
508 return (0, _preact.h)(
509 "div",
510 { "class": "hf-small-margin" },
511 (0, _preact.h)(
512 "label",
513 { "for": "hf-fg-default-value" },
514 "Default value ",
515 (0, _preact.h)(
516 "span",
517 { "class": "hf-italic hf-pull-right" },
518 "Optional"
519 )
520 ),
521 (0, _preact.h)("input", { id: "hf-fg-default-value", type: "text", value: props.value, onChange: props.onChange }),
522 (0, _preact.h)(
523 "p",
524 { "class": "help" },
525 "Text to pre-fill this field with."
526 )
527 );
528 }
529
530 function Wrap(props) {
531 return (0, _preact.h)(
532 "div",
533 { "class": "hf-small-margin" },
534 (0, _preact.h)(
535 "label",
536 { "class": "inline" },
537 (0, _preact.h)("input", { type: "checkbox", value: "1", defaultChecked: props.checked, onChange: props.onChange }),
538 "Wrap this field in paragraph tags."
539 )
540 );
541 }
542
543 function Required(props) {
544 return (0, _preact.h)(
545 "div",
546 { "class": "hf-small-margin" },
547 (0, _preact.h)(
548 "label",
549 { "class": "inline" },
550 (0, _preact.h)("input", { type: "checkbox", value: "1", defaultChecked: props.checked, onChange: props.onChange }),
551 "This field is required."
552 )
553 );
554 }
555
556 function Choices(props) {
557 var choiceFields = props.choices.map(function (choice, k) {
558 return (0, _preact.h)(
559 "div",
560 { "data-key": k },
561 (0, _preact.h)("input", { type: props.multiple ? "checkbox" : "radio", name: "selected", defaultChecked: choice.checked, onChange: props.handlers.toggleChecked, title: "Pre-select this choice?" }),
562 (0, _preact.h)("input", { type: "text", value: choice.label, placeholder: "Choice label", style: "width: 80%;", onChange: props.handlers.changeLabel }),
563 (0, _preact.h)(
564 "a",
565 { href: "javascript:void(0);", onClick: props.handlers.delete, style: "text-decoration: none;", title: "Delete choice" },
566 "\u2715"
567 )
568 );
569 });
570
571 return (0, _preact.h)(
572 "div",
573 { "class": "hf-small-margin" },
574 (0, _preact.h)(
575 "label",
576 null,
577 "Choices"
578 ),
579 choiceFields,
580 (0, _preact.h)("input", { type: props.multiple ? "checkbox" : "radio", style: "visibility: hidden;" }),
581 (0, _preact.h)(
582 "a",
583 { href: "javascript:void(0);", onClick: props.handlers.add },
584 "Add choice"
585 )
586 );
587 }
588
589 exports.AddToForm = AddToForm;
590 exports.Label = Label;
591 exports.Placeholder = Placeholder;
592 exports.DefaultValue = DefaultValue;
593 exports.Wrap = Wrap;
594 exports.Required = Required;
595 exports.Choices = Choices;
596 exports.ButtonText = ButtonText;
597
598 },{"preact":20}],5:[function(require,module,exports){
599 'use strict';
600
601 Object.defineProperty(exports, "__esModule", {
602 value: true
603 });
604 exports.htmlgenerate = undefined;
605
606 var _preactRenderToString = require('preact-render-to-string');
607
608 var _preactRenderToString2 = _interopRequireDefault(_preactRenderToString);
609
610 var _preact = require('preact');
611
612 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
613
614 function htmlgenerate(conf) {
615 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)("label", {}, conf.fieldLabel) : "";
616 var fieldAttr = void 0,
617 field = void 0;
618
619 switch (conf.fieldType) {
620 case "text":
621 default:
622 fieldAttr = {
623 type: conf.fieldType,
624 name: namify(conf.fieldLabel),
625 value: conf.value,
626 placeholder: conf.placeholder,
627 required: conf.required
628 };
629 field = html("input", fieldAttr);
630 break;
631 case "textarea":
632 fieldAttr = {
633 name: namify(conf.fieldLabel),
634 placeholder: conf.placeholder,
635 required: conf.required
636 };
637 field = html("textarea", fieldAttr, conf.value);
638 break;
639
640 case "dropdown":
641 fieldAttr = {
642 name: namify(conf.fieldLabel),
643 required: conf.required
644 };
645 var opts = conf.choices.map(function (choice) {
646 return html("option", { defaultChecked: choice.checked }, choice.label);
647 });
648 field = html("select", fieldAttr, opts);
649 break;
650
651 case "radio-buttons":
652 field = conf.choices.map(function (choice) {
653 return html("label", {}, [html("input", {
654 type: "radio",
655 name: namify(conf.fieldLabel),
656 value: choice.label,
657 selected: choice.checked
658 }), " ", html("span", {}, choice.label)]);
659 });
660 break;
661
662 case "checkboxes":
663 field = conf.choices.map(function (choice) {
664 return html("label", {}, [html("input", {
665 type: "checkbox",
666 name: namify(conf.fieldLabel) + "[]",
667 value: choice.label,
668 checked: choice.checked
669 }), " ", html("span", {}, choice.label)]);
670 });
671 break;
672
673 case "submit":
674 fieldAttr = {
675 type: "submit",
676 value: conf.value
677 };
678 field = html("input", fieldAttr);
679 break;
680
681 }
682
683 var str = "";
684 if (conf.wrap) {
685 var tmpl = (0, _preact.h)("p", {}, [label, field]);
686 str = (0, _preactRenderToString2.default)(tmpl, null, { pretty: true });
687 } else {
688 str += (0, _preactRenderToString2.default)(label, null, { pretty: true });
689 str += "\n";
690 str += (0, _preactRenderToString2.default)(field, null, { pretty: true });
691 }
692
693 return str;
694 }
695
696 function html(tag, attr, children) {
697 return (0, _preact.h)(tag, filterEmptyObjectValues(attr), children);
698 }
699
700 function namify(str) {
701 return str.replace(/ /g, '_').replace(/[^\w\[\]_]*/g, "").toUpperCase();
702 }
703
704 function filterEmptyObjectValues(obj) {
705 var newObj = {};
706 for (var propName in obj) {
707 if (obj[propName] !== false && obj[propName] !== "") {
708 newObj[propName] = obj[propName];
709 }
710 }
711 return newObj;
712 }
713
714 exports.htmlgenerate = htmlgenerate;
715
716 },{"preact":20,"preact-render-to-string":19}],6:[function(require,module,exports){
717 'use strict';
718
719 Object.defineProperty(exports, "__esModule", {
720 value: true
721 });
722 var availableActions = void 0,
723 actionTemplates = void 0,
724 actions = void 0;
725
726 function init() {
727 actions = document.getElementById('hf-form-actions');
728 availableActions = document.getElementById('hf-available-form-actions');
729 actionTemplates = document.getElementById('hf-form-action-templates');
730
731 // turn settings into accordions
732 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
733 el.parentNode.removeChild(el);
734
735 var heading = el.getAttribute('data-title');
736 var summary = el.querySelector('.hf-action-summary');
737 if (summary) {
738 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
739 }
740 var wrap = createAccordion(heading, el.innerHTML);
741 actions.appendChild(wrap);
742
743 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
744 });
745
746 availableActions.addEventListener('click', addAction, true);
747 }
748
749 function createAccordion(headingHTML, contentHTML) {
750 var wrap = document.createElement('div');
751 wrap.className = "hf-accordion expanded ";
752
753 var heading = document.createElement('h4');
754 heading.className = "hf-accordion-heading";
755 heading.innerHTML = headingHTML;
756 wrap.appendChild(heading);
757
758 var content = document.createElement('div');
759 content.className = "hf-accordion-content";
760 content.innerHTML = contentHTML;
761 wrap.appendChild(content);
762
763 var deleteWrap = document.createElement('p');
764 deleteWrap.style.textAlign = 'right';
765 var deleteLink = document.createElement('a');
766 deleteLink.href = 'javascript:void(0);';
767 deleteLink.className = "danger";
768 deleteLink.innerText = 'Delete this action';
769 deleteWrap.appendChild(deleteLink);
770 content.appendChild(deleteWrap);
771
772 // bind handlers
773 heading.addEventListener('click', createToggleActionHandler(wrap, content));
774 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
775 return wrap;
776 }
777
778 function addAction(e) {
779 var el = e.target || e.srcElement;
780 if (el.tagName !== 'INPUT') {
781 return;
782 }
783
784 var actionType = el.getAttribute('data-action-type');
785 var actionTemplate = actionTemplates.querySelector('#hf-action-type-' + actionType + '-template');
786
787 // append HTML to actions wrapper
788 var index = actions.querySelectorAll('div').length - 1;
789 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
790 actions.appendChild(wrap);
791
792 // hide "no form actions" message
793 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
794 }
795
796 function createDeleteActionHandler(wrap) {
797 return function () {
798 actions.removeChild(wrap);
799
800 if (actions.childElementCount === 1) {
801 actions.querySelector('#hf-form-actions-empty').style.display = '';
802 }
803 };
804 }
805
806 function createToggleActionHandler(wrap, content) {
807 return function () {
808 var show = content.offsetParent === null;
809 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
810 content.style.display = show ? 'block' : 'none';
811 };
812 }
813
814 exports.default = { init: init };
815
816 },{}],7:[function(require,module,exports){
817 'use strict';
818
819 // load CodeMirror & plugins
820
821 Object.defineProperty(exports, "__esModule", {
822 value: true
823 });
824 var CodeMirror = require('codemirror');
825 require('codemirror/mode/xml/xml');
826 require('codemirror/mode/javascript/javascript');
827 require('codemirror/mode/css/css');
828 require('codemirror/mode/htmlmixed/htmlmixed');
829 require('codemirror/addon/fold/xml-fold');
830 require('codemirror/addon/edit/matchtags');
831 require('codemirror/addon/edit/closetag.js');
832
833 var editor = void 0,
834 element = void 0,
835 dom = void 0,
836 requiredFieldsInput = void 0,
837 emailFieldsInput = void 0,
838 previewFrame = void 0,
839 previewDom = void 0;
840 var templateRegex = /\{\{ *(\w+)(?:\.([\w\.]+))? *(?:\|\| *(\w+))? *\}\}/g;
841
842 function init() {
843 previewFrame = document.getElementById('hf-form-preview');
844 element = document.getElementById('hf-form-editor');
845 dom = document.createElement('form');
846 requiredFieldsInput = document.getElementById('hf-required-fields');
847 emailFieldsInput = document.getElementById('hf-email-fields');
848
849 dom.innerHTML = element.value;
850 editor = CodeMirror.fromTextArea(element, {
851 selectionPointer: true,
852 matchTags: { bothTags: true },
853 mode: "htmlmixed",
854 htmlMode: true,
855 autoCloseTags: true,
856 autoRefresh: true,
857 styleActiveLine: true,
858 matchBrackets: true
859 });
860
861 editor.on('changes', debounce(updatePreview, 500));
862 editor.on('changes', debounce(updateShadowDOM, 100));
863 editor.on('changes', debounce(updateFieldVariables, 500));
864 editor.on('blur', updatePreview);
865 editor.on('blur', updateShadowDOM);
866 editor.on('blur', updateFieldVariables);
867 editor.on('blur', updateRequiredFields);
868 editor.on('blur', updateEmailFields);
869
870 previewFrame.addEventListener('load', setPreviewDom);
871 setPreviewDom();
872 updateFieldVariables();
873 }
874
875 function setPreviewDom() {
876 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
877 previewDom = frameContent.querySelector('.hf-fields-wrap');
878
879 if (previewDom) {
880 updatePreview();
881 }
882 }
883
884 function getFieldVariableName(f) {
885 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
886 }
887
888 function updateFieldVariables() {
889 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
890 var fieldVariables = uniq([].map.call(fields, function (f) {
891 return '[' + getFieldVariableName(f) + ']';
892 }));
893 var wpbody = document.getElementById('wpbody-content');
894
895 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
896 // remove existing variables
897 while (el.firstChild) {
898 el.removeChild(el.firstChild);
899 }
900
901 var variableElements = fieldVariables.map(function (n) {
902 // measure width of actual font size for prettiness
903 var sizeEl = document.createElement('span');
904 sizeEl.style.visibility = 'hidden';
905 sizeEl.innerText = n;
906 wpbody.appendChild(sizeEl);
907 var width = sizeEl.offsetWidth;
908 wpbody.removeChild(sizeEl);
909
910 // add input el
911 var el = document.createElement('input');
912 el.setAttribute('type', 'text');
913 el.style.maxWidth = width * 1.1 + 14 + 'px';
914 el.setAttribute('value', n);
915 el.setAttribute('readonly', true);
916 el.setAttribute('onfocus', 'this.select()');
917 return el;
918 });
919
920 variableElements.forEach(function (vel, i, arr) {
921 el.appendChild(vel);
922 });
923 });
924 }
925
926 function updatePreview() {
927 var markup = editor.getValue();
928
929 // replace template tags
930 markup = markup.replace(templateRegex, function (s, m) {
931 if (arguments[3]) {
932 return arguments[3];
933 }
934
935 return '';
936 });
937
938 // update dom
939 previewDom.innerHTML = markup;
940 previewDom.dispatchEvent(new Event('hf-refresh'));
941 }
942
943 function updateShadowDOM() {
944 dom.innerHTML = editor.getValue();
945 }
946
947 function updateRequiredFields() {
948 var fields = dom.querySelectorAll('[required]');
949 var fieldNames = [].map.call(fields, getFieldVariableName);
950 requiredFieldsInput.value = fieldNames.join(',');
951 }
952
953 function updateEmailFields() {
954 var fields = dom.querySelectorAll('input[type="email"]');
955 var fieldNames = [].map.call(fields, getFieldVariableName);
956 emailFieldsInput.value = fieldNames.join(',');
957 }
958
959 function replaceSelection(str) {
960 editor.replaceSelection(str);
961 editor.focus();
962 }
963
964 function debounce(func, wait, immediate) {
965 var timeout;
966 return function () {
967 var context = this,
968 args = arguments;
969 var later = function later() {
970 timeout = null;
971 if (!immediate) func.apply(context, args);
972 };
973 var callNow = immediate && !timeout;
974 clearTimeout(timeout);
975 timeout = setTimeout(later, wait);
976 if (callNow) func.apply(context, args);
977 };
978 };
979
980 function uniq(a) {
981 var seen = {};
982 return a.filter(function (item) {
983 return seen.hasOwnProperty(item) ? false : seen[item] = true;
984 });
985 }
986
987 exports.default = {
988 init: init,
989 replaceSelection: replaceSelection
990 };
991
992 },{"codemirror":12,"codemirror/addon/edit/closetag.js":9,"codemirror/addon/edit/matchtags":10,"codemirror/addon/fold/xml-fold":11,"codemirror/mode/css/css":13,"codemirror/mode/htmlmixed/htmlmixed":14,"codemirror/mode/javascript/javascript":15,"codemirror/mode/xml/xml":16}],8:[function(require,module,exports){
993 'use strict';
994
995 Object.defineProperty(exports, "__esModule", {
996 value: true
997 });
998 var tabs = void 0,
999 tabNavs = void 0;
1000
1001 function init() {
1002 tabs = document.querySelectorAll('.hf-tab');
1003 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1004 for (var i = 0; i < tabNavs.length; i++) {
1005 tabNavs[i].addEventListener('click', open);
1006 }
1007 };
1008
1009 function open(e) {
1010 var tabTarget = this.getAttribute('data-tab-target');
1011 for (var i = 0; i < tabNavs.length; i++) {
1012 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1013 }
1014 this.blur();
1015
1016 for (var _i = 0; _i < tabs.length; _i++) {
1017 var tab = tabs[_i];
1018 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1019 }
1020
1021 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + " ");
1022
1023 if (window.history) {
1024 var newUrl = window.location.href;
1025 newUrl = newUrl.replace(/\&tab\=\w+/, "");
1026 newUrl += "&tab=" + tabTarget;
1027
1028 window.history.replaceState({ tab: tabTarget }, document.title, newUrl);
1029 }
1030
1031 e.preventDefault();
1032 };
1033
1034 exports.default = {
1035 init: init,
1036 open: open
1037 };
1038
1039 },{}],9:[function(require,module,exports){
1040 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1041 // Distributed under an MIT license: http://codemirror.net/LICENSE
1042
1043 /**
1044 * Tag-closer extension for CodeMirror.
1045 *
1046 * This extension adds an "autoCloseTags" option that can be set to
1047 * either true to get the default behavior, or an object to further
1048 * configure its behavior.
1049 *
1050 * These are supported options:
1051 *
1052 * `whenClosing` (default true)
1053 * Whether to autoclose when the '/' of a closing tag is typed.
1054 * `whenOpening` (default true)
1055 * Whether to autoclose the tag when the final '>' of an opening
1056 * tag is typed.
1057 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1058 * An array of tag names that should not be autoclosed.
1059 * `indentTags` (default is block tags for HTML, none for XML)
1060 * An array of tag names that should, when opened, cause a
1061 * blank line to be added inside the tag, and the blank line and
1062 * closing line to be indented.
1063 *
1064 * See demos/closetag.html for a usage example.
1065 */
1066
1067 (function(mod) {
1068 if (typeof exports == "object" && typeof module == "object") // CommonJS
1069 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1070 else if (typeof define == "function" && define.amd) // AMD
1071 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1072 else // Plain browser env
1073 mod(CodeMirror);
1074 })(function(CodeMirror) {
1075 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1076 if (old != CodeMirror.Init && old)
1077 cm.removeKeyMap("autoCloseTags");
1078 if (!val) return;
1079 var map = {name: "autoCloseTags"};
1080 if (typeof val != "object" || val.whenClosing)
1081 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1082 if (typeof val != "object" || val.whenOpening)
1083 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1084 cm.addKeyMap(map);
1085 });
1086
1087 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1088 "source", "track", "wbr"];
1089 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1090 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1091
1092 function autoCloseGT(cm) {
1093 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1094 var ranges = cm.listSelections(), replacements = [];
1095 var opt = cm.getOption("autoCloseTags");
1096 for (var i = 0; i < ranges.length; i++) {
1097 if (!ranges[i].empty()) return CodeMirror.Pass;
1098 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1099 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1100 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1101
1102 var html = inner.mode.configuration == "html";
1103 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1104 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1105
1106 var tagName = state.tagName;
1107 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1108 var lowerTagName = tagName.toLowerCase();
1109 // Don't process the '>' at the end of an end-tag or self-closing tag
1110 if (!tagName ||
1111 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1112 tok.type == "tag" && state.type == "closeTag" ||
1113 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1114 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1115 closingTagExists(cm, tagName, pos, state, true))
1116 return CodeMirror.Pass;
1117
1118 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1119 replacements[i] = {indent: indent,
1120 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1121 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1122 }
1123
1124 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1125 for (var i = ranges.length - 1; i >= 0; i--) {
1126 var info = replacements[i];
1127 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1128 var sel = cm.listSelections().slice(0);
1129 sel[i] = {head: info.newPos, anchor: info.newPos};
1130 cm.setSelections(sel);
1131 if (!dontIndentOnAutoClose && info.indent) {
1132 cm.indentLine(info.newPos.line, null, true);
1133 cm.indentLine(info.newPos.line + 1, null, true);
1134 }
1135 }
1136 }
1137
1138 function autoCloseCurrent(cm, typingSlash) {
1139 var ranges = cm.listSelections(), replacements = [];
1140 var head = typingSlash ? "/" : "</";
1141 var opt = cm.getOption("autoCloseTags");
1142 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1143 for (var i = 0; i < ranges.length; i++) {
1144 if (!ranges[i].empty()) return CodeMirror.Pass;
1145 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1146 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1147 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1148 tok.start != pos.ch - 1))
1149 return CodeMirror.Pass;
1150 // Kludge to get around the fact that we are not in XML mode
1151 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1152 // work for other XML embedded languages (there is no general
1153 // way to go from a mixed mode to its current XML state).
1154 var replacement;
1155 if (inner.mode.name != "xml") {
1156 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1157 replacement = head + "script";
1158 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1159 replacement = head + "style";
1160 else
1161 return CodeMirror.Pass;
1162 } else {
1163 if (!state.context || !state.context.tagName ||
1164 closingTagExists(cm, state.context.tagName, pos, state))
1165 return CodeMirror.Pass;
1166 replacement = head + state.context.tagName;
1167 }
1168 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1169 replacements[i] = replacement;
1170 }
1171 cm.replaceSelections(replacements);
1172 ranges = cm.listSelections();
1173 if (!dontIndentOnAutoClose) {
1174 for (var i = 0; i < ranges.length; i++)
1175 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1176 cm.indentLine(ranges[i].head.line);
1177 }
1178 }
1179
1180 function autoCloseSlash(cm) {
1181 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1182 return autoCloseCurrent(cm, true);
1183 }
1184
1185 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1186
1187 function indexOf(collection, elt) {
1188 if (collection.indexOf) return collection.indexOf(elt);
1189 for (var i = 0, e = collection.length; i < e; ++i)
1190 if (collection[i] == elt) return i;
1191 return -1;
1192 }
1193
1194 // If xml-fold is loaded, we use its functionality to try and verify
1195 // whether a given tag is actually unclosed.
1196 function closingTagExists(cm, tagName, pos, state, newTag) {
1197 if (!CodeMirror.scanForClosingTag) return false;
1198 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1199 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1200 if (!nextClose || nextClose.tag != tagName) return false;
1201 var cx = state.context;
1202 // If the immediate wrapping context contains onCx instances of
1203 // the same tag, a closing tag only exists if there are at least
1204 // that many closing tags of that type following.
1205 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1206 pos = nextClose.to;
1207 for (var i = 1; i < onCx; i++) {
1208 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1209 if (!next || next.tag != tagName) return false;
1210 pos = next.to;
1211 }
1212 return true;
1213 }
1214 });
1215
1216 },{"../../lib/codemirror":12,"../fold/xml-fold":11}],10:[function(require,module,exports){
1217 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1218 // Distributed under an MIT license: http://codemirror.net/LICENSE
1219
1220 (function(mod) {
1221 if (typeof exports == "object" && typeof module == "object") // CommonJS
1222 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1223 else if (typeof define == "function" && define.amd) // AMD
1224 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1225 else // Plain browser env
1226 mod(CodeMirror);
1227 })(function(CodeMirror) {
1228 "use strict";
1229
1230 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1231 if (old && old != CodeMirror.Init) {
1232 cm.off("cursorActivity", doMatchTags);
1233 cm.off("viewportChange", maybeUpdateMatch);
1234 clear(cm);
1235 }
1236 if (val) {
1237 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1238 cm.on("cursorActivity", doMatchTags);
1239 cm.on("viewportChange", maybeUpdateMatch);
1240 doMatchTags(cm);
1241 }
1242 });
1243
1244 function clear(cm) {
1245 if (cm.state.tagHit) cm.state.tagHit.clear();
1246 if (cm.state.tagOther) cm.state.tagOther.clear();
1247 cm.state.tagHit = cm.state.tagOther = null;
1248 }
1249
1250 function doMatchTags(cm) {
1251 cm.state.failedTagMatch = false;
1252 cm.operation(function() {
1253 clear(cm);
1254 if (cm.somethingSelected()) return;
1255 var cur = cm.getCursor(), range = cm.getViewport();
1256 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1257 var match = CodeMirror.findMatchingTag(cm, cur, range);
1258 if (!match) return;
1259 if (cm.state.matchBothTags) {
1260 var hit = match.at == "open" ? match.open : match.close;
1261 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1262 }
1263 var other = match.at == "close" ? match.open : match.close;
1264 if (other)
1265 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1266 else
1267 cm.state.failedTagMatch = true;
1268 });
1269 }
1270
1271 function maybeUpdateMatch(cm) {
1272 if (cm.state.failedTagMatch) doMatchTags(cm);
1273 }
1274
1275 CodeMirror.commands.toMatchingTag = function(cm) {
1276 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1277 if (found) {
1278 var other = found.at == "close" ? found.open : found.close;
1279 if (other) cm.extendSelection(other.to, other.from);
1280 }
1281 };
1282 });
1283
1284 },{"../../lib/codemirror":12,"../fold/xml-fold":11}],11:[function(require,module,exports){
1285 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1286 // Distributed under an MIT license: http://codemirror.net/LICENSE
1287
1288 (function(mod) {
1289 if (typeof exports == "object" && typeof module == "object") // CommonJS
1290 mod(require("../../lib/codemirror"));
1291 else if (typeof define == "function" && define.amd) // AMD
1292 define(["../../lib/codemirror"], mod);
1293 else // Plain browser env
1294 mod(CodeMirror);
1295 })(function(CodeMirror) {
1296 "use strict";
1297
1298 var Pos = CodeMirror.Pos;
1299 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1300
1301 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";
1302 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1303 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1304
1305 function Iter(cm, line, ch, range) {
1306 this.line = line; this.ch = ch;
1307 this.cm = cm; this.text = cm.getLine(line);
1308 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1309 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1310 }
1311
1312 function tagAt(iter, ch) {
1313 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1314 return type && /\btag\b/.test(type);
1315 }
1316
1317 function nextLine(iter) {
1318 if (iter.line >= iter.max) return;
1319 iter.ch = 0;
1320 iter.text = iter.cm.getLine(++iter.line);
1321 return true;
1322 }
1323 function prevLine(iter) {
1324 if (iter.line <= iter.min) return;
1325 iter.text = iter.cm.getLine(--iter.line);
1326 iter.ch = iter.text.length;
1327 return true;
1328 }
1329
1330 function toTagEnd(iter) {
1331 for (;;) {
1332 var gt = iter.text.indexOf(">", iter.ch);
1333 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1334 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1335 var lastSlash = iter.text.lastIndexOf("/", gt);
1336 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1337 iter.ch = gt + 1;
1338 return selfClose ? "selfClose" : "regular";
1339 }
1340 }
1341 function toTagStart(iter) {
1342 for (;;) {
1343 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1344 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1345 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1346 xmlTagStart.lastIndex = lt;
1347 iter.ch = lt;
1348 var match = xmlTagStart.exec(iter.text);
1349 if (match && match.index == lt) return match;
1350 }
1351 }
1352
1353 function toNextTag(iter) {
1354 for (;;) {
1355 xmlTagStart.lastIndex = iter.ch;
1356 var found = xmlTagStart.exec(iter.text);
1357 if (!found) { if (nextLine(iter)) continue; else return; }
1358 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1359 iter.ch = found.index + found[0].length;
1360 return found;
1361 }
1362 }
1363 function toPrevTag(iter) {
1364 for (;;) {
1365 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1366 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1367 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1368 var lastSlash = iter.text.lastIndexOf("/", gt);
1369 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1370 iter.ch = gt + 1;
1371 return selfClose ? "selfClose" : "regular";
1372 }
1373 }
1374
1375 function findMatchingClose(iter, tag) {
1376 var stack = [];
1377 for (;;) {
1378 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1379 if (!next || !(end = toTagEnd(iter))) return;
1380 if (end == "selfClose") continue;
1381 if (next[1]) { // closing tag
1382 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1383 stack.length = i;
1384 break;
1385 }
1386 if (i < 0 && (!tag || tag == next[2])) return {
1387 tag: next[2],
1388 from: Pos(startLine, startCh),
1389 to: Pos(iter.line, iter.ch)
1390 };
1391 } else { // opening tag
1392 stack.push(next[2]);
1393 }
1394 }
1395 }
1396 function findMatchingOpen(iter, tag) {
1397 var stack = [];
1398 for (;;) {
1399 var prev = toPrevTag(iter);
1400 if (!prev) return;
1401 if (prev == "selfClose") { toTagStart(iter); continue; }
1402 var endLine = iter.line, endCh = iter.ch;
1403 var start = toTagStart(iter);
1404 if (!start) return;
1405 if (start[1]) { // closing tag
1406 stack.push(start[2]);
1407 } else { // opening tag
1408 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1409 stack.length = i;
1410 break;
1411 }
1412 if (i < 0 && (!tag || tag == start[2])) return {
1413 tag: start[2],
1414 from: Pos(iter.line, iter.ch),
1415 to: Pos(endLine, endCh)
1416 };
1417 }
1418 }
1419 }
1420
1421 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1422 var iter = new Iter(cm, start.line, 0);
1423 for (;;) {
1424 var openTag = toNextTag(iter), end;
1425 if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
1426 if (!openTag[1] && end != "selfClose") {
1427 var startPos = Pos(iter.line, iter.ch);
1428 var endPos = findMatchingClose(iter, openTag[2]);
1429 return endPos && {from: startPos, to: endPos.from};
1430 }
1431 }
1432 });
1433 CodeMirror.findMatchingTag = function(cm, pos, range) {
1434 var iter = new Iter(cm, pos.line, pos.ch, range);
1435 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1436 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1437 var start = end && toTagStart(iter);
1438 if (!end || !start || cmp(iter, pos) > 0) return;
1439 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1440 if (end == "selfClose") return {open: here, close: null, at: "open"};
1441
1442 if (start[1]) { // closing tag
1443 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1444 } else { // opening tag
1445 iter = new Iter(cm, to.line, to.ch, range);
1446 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1447 }
1448 };
1449
1450 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1451 var iter = new Iter(cm, pos.line, pos.ch, range);
1452 for (;;) {
1453 var open = findMatchingOpen(iter, tag);
1454 if (!open) break;
1455 var forward = new Iter(cm, pos.line, pos.ch, range);
1456 var close = findMatchingClose(forward, open.tag);
1457 if (close) return {open: open, close: close};
1458 }
1459 };
1460
1461 // Used by addon/edit/closetag.js
1462 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1463 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1464 return findMatchingClose(iter, name);
1465 };
1466 });
1467
1468 },{"../../lib/codemirror":12}],12:[function(require,module,exports){
1469 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1470 // Distributed under an MIT license: http://codemirror.net/LICENSE
1471
1472 // This is CodeMirror (http://codemirror.net), a code editor
1473 // implemented in JavaScript on top of the browser's DOM.
1474 //
1475 // You can find some technical background for some of the code below
1476 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1477
1478 (function (global, factory) {
1479 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1480 typeof define === 'function' && define.amd ? define(factory) :
1481 (global.CodeMirror = factory());
1482 }(this, (function () { 'use strict';
1483
1484 // Kludges for bugs and behavior differences that can't be feature
1485 // detected are enabled based on userAgent etc sniffing.
1486 var userAgent = navigator.userAgent;
1487 var platform = navigator.platform;
1488
1489 var gecko = /gecko\/\d/i.test(userAgent);
1490 var ie_upto10 = /MSIE \d/.test(userAgent);
1491 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1492 var edge = /Edge\/(\d+)/.exec(userAgent);
1493 var ie = ie_upto10 || ie_11up || edge;
1494 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1495 var webkit = !edge && /WebKit\//.test(userAgent);
1496 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1497 var chrome = !edge && /Chrome\//.test(userAgent);
1498 var presto = /Opera\//.test(userAgent);
1499 var safari = /Apple Computer/.test(navigator.vendor);
1500 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1501 var phantom = /PhantomJS/.test(userAgent);
1502
1503 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1504 var android = /Android/.test(userAgent);
1505 // This is woefully incomplete. Suggestions for alternative methods welcome.
1506 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1507 var mac = ios || /Mac/.test(platform);
1508 var chromeOS = /\bCrOS\b/.test(userAgent);
1509 var windows = /win/i.test(platform);
1510
1511 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1512 if (presto_version) { presto_version = Number(presto_version[1]); }
1513 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1514 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1515 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1516 var captureRightClick = gecko || (ie && ie_version >= 9);
1517
1518 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1519
1520 var rmClass = function(node, cls) {
1521 var current = node.className;
1522 var match = classTest(cls).exec(current);
1523 if (match) {
1524 var after = current.slice(match.index + match[0].length);
1525 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1526 }
1527 };
1528
1529 function removeChildren(e) {
1530 for (var count = e.childNodes.length; count > 0; --count)
1531 { e.removeChild(e.firstChild); }
1532 return e
1533 }
1534
1535 function removeChildrenAndAdd(parent, e) {
1536 return removeChildren(parent).appendChild(e)
1537 }
1538
1539 function elt(tag, content, className, style) {
1540 var e = document.createElement(tag);
1541 if (className) { e.className = className; }
1542 if (style) { e.style.cssText = style; }
1543 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1544 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1545 return e
1546 }
1547 // wrapper for elt, which removes the elt from the accessibility tree
1548 function eltP(tag, content, className, style) {
1549 var e = elt(tag, content, className, style);
1550 e.setAttribute("role", "presentation");
1551 return e
1552 }
1553
1554 var range;
1555 if (document.createRange) { range = function(node, start, end, endNode) {
1556 var r = document.createRange();
1557 r.setEnd(endNode || node, end);
1558 r.setStart(node, start);
1559 return r
1560 }; }
1561 else { range = function(node, start, end) {
1562 var r = document.body.createTextRange();
1563 try { r.moveToElementText(node.parentNode); }
1564 catch(e) { return r }
1565 r.collapse(true);
1566 r.moveEnd("character", end);
1567 r.moveStart("character", start);
1568 return r
1569 }; }
1570
1571 function contains(parent, child) {
1572 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1573 { child = child.parentNode; }
1574 if (parent.contains)
1575 { return parent.contains(child) }
1576 do {
1577 if (child.nodeType == 11) { child = child.host; }
1578 if (child == parent) { return true }
1579 } while (child = child.parentNode)
1580 }
1581
1582 function activeElt() {
1583 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1584 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1585 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1586 var activeElement;
1587 try {
1588 activeElement = document.activeElement;
1589 } catch(e) {
1590 activeElement = document.body || null;
1591 }
1592 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1593 { activeElement = activeElement.shadowRoot.activeElement; }
1594 return activeElement
1595 }
1596
1597 function addClass(node, cls) {
1598 var current = node.className;
1599 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1600 }
1601 function joinClasses(a, b) {
1602 var as = a.split(" ");
1603 for (var i = 0; i < as.length; i++)
1604 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1605 return b
1606 }
1607
1608 var selectInput = function(node) { node.select(); };
1609 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1610 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1611 else if (ie) // Suppress mysterious IE10 errors
1612 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1613
1614 function bind(f) {
1615 var args = Array.prototype.slice.call(arguments, 1);
1616 return function(){return f.apply(null, args)}
1617 }
1618
1619 function copyObj(obj, target, overwrite) {
1620 if (!target) { target = {}; }
1621 for (var prop in obj)
1622 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1623 { target[prop] = obj[prop]; } }
1624 return target
1625 }
1626
1627 // Counts the column offset in a string, taking tabs into account.
1628 // Used mostly to find indentation.
1629 function countColumn(string, end, tabSize, startIndex, startValue) {
1630 if (end == null) {
1631 end = string.search(/[^\s\u00a0]/);
1632 if (end == -1) { end = string.length; }
1633 }
1634 for (var i = startIndex || 0, n = startValue || 0;;) {
1635 var nextTab = string.indexOf("\t", i);
1636 if (nextTab < 0 || nextTab >= end)
1637 { return n + (end - i) }
1638 n += nextTab - i;
1639 n += tabSize - (n % tabSize);
1640 i = nextTab + 1;
1641 }
1642 }
1643
1644 var Delayed = function() {this.id = null;};
1645 Delayed.prototype.set = function (ms, f) {
1646 clearTimeout(this.id);
1647 this.id = setTimeout(f, ms);
1648 };
1649
1650 function indexOf(array, elt) {
1651 for (var i = 0; i < array.length; ++i)
1652 { if (array[i] == elt) { return i } }
1653 return -1
1654 }
1655
1656 // Number of pixels added to scroller and sizer to hide scrollbar
1657 var scrollerGap = 30;
1658
1659 // Returned or thrown by various protocols to signal 'I'm not
1660 // handling this'.
1661 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1662
1663 // Reused option objects for setSelection & friends
1664 var sel_dontScroll = {scroll: false};
1665 var sel_mouse = {origin: "*mouse"};
1666 var sel_move = {origin: "+move"};
1667
1668 // The inverse of countColumn -- find the offset that corresponds to
1669 // a particular column.
1670 function findColumn(string, goal, tabSize) {
1671 for (var pos = 0, col = 0;;) {
1672 var nextTab = string.indexOf("\t", pos);
1673 if (nextTab == -1) { nextTab = string.length; }
1674 var skipped = nextTab - pos;
1675 if (nextTab == string.length || col + skipped >= goal)
1676 { return pos + Math.min(skipped, goal - col) }
1677 col += nextTab - pos;
1678 col += tabSize - (col % tabSize);
1679 pos = nextTab + 1;
1680 if (col >= goal) { return pos }
1681 }
1682 }
1683
1684 var spaceStrs = [""];
1685 function spaceStr(n) {
1686 while (spaceStrs.length <= n)
1687 { spaceStrs.push(lst(spaceStrs) + " "); }
1688 return spaceStrs[n]
1689 }
1690
1691 function lst(arr) { return arr[arr.length-1] }
1692
1693 function map(array, f) {
1694 var out = [];
1695 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1696 return out
1697 }
1698
1699 function insertSorted(array, value, score) {
1700 var pos = 0, priority = score(value);
1701 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1702 array.splice(pos, 0, value);
1703 }
1704
1705 function nothing() {}
1706
1707 function createObj(base, props) {
1708 var inst;
1709 if (Object.create) {
1710 inst = Object.create(base);
1711 } else {
1712 nothing.prototype = base;
1713 inst = new nothing();
1714 }
1715 if (props) { copyObj(props, inst); }
1716 return inst
1717 }
1718
1719 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1720 function isWordCharBasic(ch) {
1721 return /\w/.test(ch) || ch > "\x80" &&
1722 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1723 }
1724 function isWordChar(ch, helper) {
1725 if (!helper) { return isWordCharBasic(ch) }
1726 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1727 return helper.test(ch)
1728 }
1729
1730 function isEmpty(obj) {
1731 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1732 return true
1733 }
1734
1735 // Extending unicode characters. A series of a non-extending char +
1736 // any number of extending chars is treated as a single unit as far
1737 // as editing and measuring is concerned. This is not fully correct,
1738 // since some scripts/fonts/browsers also treat other configurations
1739 // of code points as a group.
1740 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]/;
1741 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1742
1743 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1744 function skipExtendingChars(str, pos, dir) {
1745 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1746 return pos
1747 }
1748
1749 // Returns the value from the range [`from`; `to`] that satisfies
1750 // `pred` and is closest to `from`. Assumes that at least `to`
1751 // satisfies `pred`. Supports `from` being greater than `to`.
1752 function findFirst(pred, from, to) {
1753 // At any point we are certain `to` satisfies `pred`, don't know
1754 // whether `from` does.
1755 var dir = from > to ? -1 : 1;
1756 for (;;) {
1757 if (from == to) { return from }
1758 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1759 if (mid == from) { return pred(mid) ? from : to }
1760 if (pred(mid)) { to = mid; }
1761 else { from = mid + dir; }
1762 }
1763 }
1764
1765 // The display handles the DOM integration, both for input reading
1766 // and content drawing. It holds references to DOM nodes and
1767 // display-related state.
1768
1769 function Display(place, doc, input) {
1770 var d = this;
1771 this.input = input;
1772
1773 // Covers bottom-right square when both scrollbars are present.
1774 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1775 d.scrollbarFiller.setAttribute("cm-not-content", "true");
1776 // Covers bottom of gutter when coverGutterNextToScrollbar is on
1777 // and h scrollbar is present.
1778 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
1779 d.gutterFiller.setAttribute("cm-not-content", "true");
1780 // Will contain the actual code, positioned to cover the viewport.
1781 d.lineDiv = eltP("div", null, "CodeMirror-code");
1782 // Elements are added to these to represent selection and cursors.
1783 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1784 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
1785 // A visibility: hidden element used to find the size of things.
1786 d.measure = elt("div", null, "CodeMirror-measure");
1787 // When lines outside of the viewport are measured, they are drawn in this.
1788 d.lineMeasure = elt("div", null, "CodeMirror-measure");
1789 // Wraps everything that needs to exist inside the vertically-padded coordinate system
1790 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
1791 null, "position: relative; outline: none");
1792 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
1793 // Moved around its parent to cover visible view.
1794 d.mover = elt("div", [lines], null, "position: relative");
1795 // Set to the height of the document, allowing scrolling.
1796 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1797 d.sizerWidth = null;
1798 // Behavior of elts with overflow: auto and padding is
1799 // inconsistent across browsers. This is used to ensure the
1800 // scrollable area is big enough.
1801 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
1802 // Will contain the gutters, if any.
1803 d.gutters = elt("div", null, "CodeMirror-gutters");
1804 d.lineGutter = null;
1805 // Actual scrollable element.
1806 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
1807 d.scroller.setAttribute("tabIndex", "-1");
1808 // The element in which the editor lives.
1809 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
1810
1811 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
1812 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1813 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
1814
1815 if (place) {
1816 if (place.appendChild) { place.appendChild(d.wrapper); }
1817 else { place(d.wrapper); }
1818 }
1819
1820 // Current rendered range (may be bigger than the view window).
1821 d.viewFrom = d.viewTo = doc.first;
1822 d.reportedViewFrom = d.reportedViewTo = doc.first;
1823 // Information about the rendered lines.
1824 d.view = [];
1825 d.renderedView = null;
1826 // Holds info about a single rendered line when it was rendered
1827 // for measurement, while not in view.
1828 d.externalMeasured = null;
1829 // Empty space (in pixels) above the view
1830 d.viewOffset = 0;
1831 d.lastWrapHeight = d.lastWrapWidth = 0;
1832 d.updateLineNumbers = null;
1833
1834 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
1835 d.scrollbarsClipped = false;
1836
1837 // Used to only resize the line number gutter when necessary (when
1838 // the amount of lines crosses a boundary that makes its width change)
1839 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1840 // Set to true when a non-horizontal-scrolling line widget is
1841 // added. As an optimization, line widget aligning is skipped when
1842 // this is false.
1843 d.alignWidgets = false;
1844
1845 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
1846
1847 // Tracks the maximum line length so that the horizontal scrollbar
1848 // can be kept static when scrolling.
1849 d.maxLine = null;
1850 d.maxLineLength = 0;
1851 d.maxLineChanged = false;
1852
1853 // Used for measuring wheel scrolling granularity
1854 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
1855
1856 // True when shift is held down.
1857 d.shift = false;
1858
1859 // Used to track whether anything happened since the context menu
1860 // was opened.
1861 d.selForContextMenu = null;
1862
1863 d.activeTouch = null;
1864
1865 input.init(d);
1866 }
1867
1868 // Find the line object corresponding to the given line number.
1869 function getLine(doc, n) {
1870 n -= doc.first;
1871 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
1872 var chunk = doc;
1873 while (!chunk.lines) {
1874 for (var i = 0;; ++i) {
1875 var child = chunk.children[i], sz = child.chunkSize();
1876 if (n < sz) { chunk = child; break }
1877 n -= sz;
1878 }
1879 }
1880 return chunk.lines[n]
1881 }
1882
1883 // Get the part of a document between two positions, as an array of
1884 // strings.
1885 function getBetween(doc, start, end) {
1886 var out = [], n = start.line;
1887 doc.iter(start.line, end.line + 1, function (line) {
1888 var text = line.text;
1889 if (n == end.line) { text = text.slice(0, end.ch); }
1890 if (n == start.line) { text = text.slice(start.ch); }
1891 out.push(text);
1892 ++n;
1893 });
1894 return out
1895 }
1896 // Get the lines between from and to, as array of strings.
1897 function getLines(doc, from, to) {
1898 var out = [];
1899 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
1900 return out
1901 }
1902
1903 // Update the height of a line, propagating the height change
1904 // upwards to parent nodes.
1905 function updateLineHeight(line, height) {
1906 var diff = height - line.height;
1907 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
1908 }
1909
1910 // Given a line object, find its line number by walking up through
1911 // its parent links.
1912 function lineNo(line) {
1913 if (line.parent == null) { return null }
1914 var cur = line.parent, no = indexOf(cur.lines, line);
1915 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
1916 for (var i = 0;; ++i) {
1917 if (chunk.children[i] == cur) { break }
1918 no += chunk.children[i].chunkSize();
1919 }
1920 }
1921 return no + cur.first
1922 }
1923
1924 // Find the line at the given vertical position, using the height
1925 // information in the document tree.
1926 function lineAtHeight(chunk, h) {
1927 var n = chunk.first;
1928 outer: do {
1929 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
1930 var child = chunk.children[i$1], ch = child.height;
1931 if (h < ch) { chunk = child; continue outer }
1932 h -= ch;
1933 n += child.chunkSize();
1934 }
1935 return n
1936 } while (!chunk.lines)
1937 var i = 0;
1938 for (; i < chunk.lines.length; ++i) {
1939 var line = chunk.lines[i], lh = line.height;
1940 if (h < lh) { break }
1941 h -= lh;
1942 }
1943 return n + i
1944 }
1945
1946 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
1947
1948 function lineNumberFor(options, i) {
1949 return String(options.lineNumberFormatter(i + options.firstLineNumber))
1950 }
1951
1952 // A Pos instance represents a position within the text.
1953 function Pos(line, ch, sticky) {
1954 if ( sticky === void 0 ) sticky = null;
1955
1956 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
1957 this.line = line;
1958 this.ch = ch;
1959 this.sticky = sticky;
1960 }
1961
1962 // Compare two positions, return 0 if they are the same, a negative
1963 // number when a is less, and a positive number otherwise.
1964 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
1965
1966 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
1967
1968 function copyPos(x) {return Pos(x.line, x.ch)}
1969 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
1970 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
1971
1972 // Most of the external API clips given positions to make sure they
1973 // actually exist within the document.
1974 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
1975 function clipPos(doc, pos) {
1976 if (pos.line < doc.first) { return Pos(doc.first, 0) }
1977 var last = doc.first + doc.size - 1;
1978 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
1979 return clipToLen(pos, getLine(doc, pos.line).text.length)
1980 }
1981 function clipToLen(pos, linelen) {
1982 var ch = pos.ch;
1983 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
1984 else if (ch < 0) { return Pos(pos.line, 0) }
1985 else { return pos }
1986 }
1987 function clipPosArray(doc, array) {
1988 var out = [];
1989 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
1990 return out
1991 }
1992
1993 // Optimize some code when these features are not used.
1994 var sawReadOnlySpans = false;
1995 var sawCollapsedSpans = false;
1996
1997 function seeReadOnlySpans() {
1998 sawReadOnlySpans = true;
1999 }
2000
2001 function seeCollapsedSpans() {
2002 sawCollapsedSpans = true;
2003 }
2004
2005 // TEXTMARKER SPANS
2006
2007 function MarkedSpan(marker, from, to) {
2008 this.marker = marker;
2009 this.from = from; this.to = to;
2010 }
2011
2012 // Search an array of spans for a span matching the given marker.
2013 function getMarkedSpanFor(spans, marker) {
2014 if (spans) { for (var i = 0; i < spans.length; ++i) {
2015 var span = spans[i];
2016 if (span.marker == marker) { return span }
2017 } }
2018 }
2019 // Remove a span from an array, returning undefined if no spans are
2020 // left (we don't store arrays for lines without spans).
2021 function removeMarkedSpan(spans, span) {
2022 var r;
2023 for (var i = 0; i < spans.length; ++i)
2024 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2025 return r
2026 }
2027 // Add a span to a line.
2028 function addMarkedSpan(line, span) {
2029 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2030 span.marker.attachLine(line);
2031 }
2032
2033 // Used for the algorithm that adjusts markers for a change in the
2034 // document. These functions cut an array of spans at a given
2035 // character position, returning an array of remaining chunks (or
2036 // undefined if nothing remains).
2037 function markedSpansBefore(old, startCh, isInsert) {
2038 var nw;
2039 if (old) { for (var i = 0; i < old.length; ++i) {
2040 var span = old[i], marker = span.marker;
2041 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2042 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2043 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
2044 }
2045 } }
2046 return nw
2047 }
2048 function markedSpansAfter(old, endCh, isInsert) {
2049 var nw;
2050 if (old) { for (var i = 0; i < old.length; ++i) {
2051 var span = old[i], marker = span.marker;
2052 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2053 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2054 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2055 span.to == null ? null : span.to - endCh));
2056 }
2057 } }
2058 return nw
2059 }
2060
2061 // Given a change object, compute the new set of marker spans that
2062 // cover the line in which the change took place. Removes spans
2063 // entirely within the change, reconnects spans belonging to the
2064 // same marker that appear on both sides of the change, and cuts off
2065 // spans partially within the change. Returns an array of span
2066 // arrays with one element for each line in (after) the change.
2067 function stretchSpansOverChange(doc, change) {
2068 if (change.full) { return null }
2069 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2070 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2071 if (!oldFirst && !oldLast) { return null }
2072
2073 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2074 // Get the spans that 'stick out' on both sides
2075 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2076 var last = markedSpansAfter(oldLast, endCh, isInsert);
2077
2078 // Next, merge those two ends
2079 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2080 if (first) {
2081 // Fix up .to properties of first
2082 for (var i = 0; i < first.length; ++i) {
2083 var span = first[i];
2084 if (span.to == null) {
2085 var found = getMarkedSpanFor(last, span.marker);
2086 if (!found) { span.to = startCh; }
2087 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2088 }
2089 }
2090 }
2091 if (last) {
2092 // Fix up .from in last (or move them into first in case of sameLine)
2093 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2094 var span$1 = last[i$1];
2095 if (span$1.to != null) { span$1.to += offset; }
2096 if (span$1.from == null) {
2097 var found$1 = getMarkedSpanFor(first, span$1.marker);
2098 if (!found$1) {
2099 span$1.from = offset;
2100 if (sameLine) { (first || (first = [])).push(span$1); }
2101 }
2102 } else {
2103 span$1.from += offset;
2104 if (sameLine) { (first || (first = [])).push(span$1); }
2105 }
2106 }
2107 }
2108 // Make sure we didn't create any zero-length spans
2109 if (first) { first = clearEmptySpans(first); }
2110 if (last && last != first) { last = clearEmptySpans(last); }
2111
2112 var newMarkers = [first];
2113 if (!sameLine) {
2114 // Fill gap with whole-line-spans
2115 var gap = change.text.length - 2, gapMarkers;
2116 if (gap > 0 && first)
2117 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2118 { if (first[i$2].to == null)
2119 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2120 for (var i$3 = 0; i$3 < gap; ++i$3)
2121 { newMarkers.push(gapMarkers); }
2122 newMarkers.push(last);
2123 }
2124 return newMarkers
2125 }
2126
2127 // Remove spans that are empty and don't have a clearWhenEmpty
2128 // option of false.
2129 function clearEmptySpans(spans) {
2130 for (var i = 0; i < spans.length; ++i) {
2131 var span = spans[i];
2132 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
2133 { spans.splice(i--, 1); }
2134 }
2135 if (!spans.length) { return null }
2136 return spans
2137 }
2138
2139 // Used to 'clip' out readOnly ranges when making a change.
2140 function removeReadOnlyRanges(doc, from, to) {
2141 var markers = null;
2142 doc.iter(from.line, to.line + 1, function (line) {
2143 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
2144 var mark = line.markedSpans[i].marker;
2145 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
2146 { (markers || (markers = [])).push(mark); }
2147 } }
2148 });
2149 if (!markers) { return null }
2150 var parts = [{from: from, to: to}];
2151 for (var i = 0; i < markers.length; ++i) {
2152 var mk = markers[i], m = mk.find(0);
2153 for (var j = 0; j < parts.length; ++j) {
2154 var p = parts[j];
2155 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
2156 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
2157 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
2158 { newParts.push({from: p.from, to: m.from}); }
2159 if (dto > 0 || !mk.inclusiveRight && !dto)
2160 { newParts.push({from: m.to, to: p.to}); }
2161 parts.splice.apply(parts, newParts);
2162 j += newParts.length - 3;
2163 }
2164 }
2165 return parts
2166 }
2167
2168 // Connect or disconnect spans from a line.
2169 function detachMarkedSpans(line) {
2170 var spans = line.markedSpans;
2171 if (!spans) { return }
2172 for (var i = 0; i < spans.length; ++i)
2173 { spans[i].marker.detachLine(line); }
2174 line.markedSpans = null;
2175 }
2176 function attachMarkedSpans(line, spans) {
2177 if (!spans) { return }
2178 for (var i = 0; i < spans.length; ++i)
2179 { spans[i].marker.attachLine(line); }
2180 line.markedSpans = spans;
2181 }
2182
2183 // Helpers used when computing which overlapping collapsed span
2184 // counts as the larger one.
2185 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
2186 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
2187
2188 // Returns a number indicating which of two overlapping collapsed
2189 // spans is larger (and thus includes the other). Falls back to
2190 // comparing ids when the spans cover exactly the same range.
2191 function compareCollapsedMarkers(a, b) {
2192 var lenDiff = a.lines.length - b.lines.length;
2193 if (lenDiff != 0) { return lenDiff }
2194 var aPos = a.find(), bPos = b.find();
2195 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
2196 if (fromCmp) { return -fromCmp }
2197 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
2198 if (toCmp) { return toCmp }
2199 return b.id - a.id
2200 }
2201
2202 // Find out whether a line ends or starts in a collapsed span. If
2203 // so, return the marker for that span.
2204 function collapsedSpanAtSide(line, start) {
2205 var sps = sawCollapsedSpans && line.markedSpans, found;
2206 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2207 sp = sps[i];
2208 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
2209 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
2210 { found = sp.marker; }
2211 } }
2212 return found
2213 }
2214 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
2215 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
2216
2217 // Test whether there exists a collapsed span that partially
2218 // overlaps (covers the start or end, but not both) of a new span.
2219 // Such overlap is not allowed.
2220 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
2221 var line = getLine(doc, lineNo$$1);
2222 var sps = sawCollapsedSpans && line.markedSpans;
2223 if (sps) { for (var i = 0; i < sps.length; ++i) {
2224 var sp = sps[i];
2225 if (!sp.marker.collapsed) { continue }
2226 var found = sp.marker.find(0);
2227 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
2228 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
2229 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
2230 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
2231 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
2232 { return true }
2233 } }
2234 }
2235
2236 // A visual line is a line as drawn on the screen. Folding, for
2237 // example, can cause multiple logical lines to appear on the same
2238 // visual line. This finds the start of the visual line that the
2239 // given line is part of (usually that is the line itself).
2240 function visualLine(line) {
2241 var merged;
2242 while (merged = collapsedSpanAtStart(line))
2243 { line = merged.find(-1, true).line; }
2244 return line
2245 }
2246
2247 function visualLineEnd(line) {
2248 var merged;
2249 while (merged = collapsedSpanAtEnd(line))
2250 { line = merged.find(1, true).line; }
2251 return line
2252 }
2253
2254 // Returns an array of logical lines that continue the visual line
2255 // started by the argument, or undefined if there are no such lines.
2256 function visualLineContinued(line) {
2257 var merged, lines;
2258 while (merged = collapsedSpanAtEnd(line)) {
2259 line = merged.find(1, true).line
2260 ;(lines || (lines = [])).push(line);
2261 }
2262 return lines
2263 }
2264
2265 // Get the line number of the start of the visual line that the
2266 // given line number is part of.
2267 function visualLineNo(doc, lineN) {
2268 var line = getLine(doc, lineN), vis = visualLine(line);
2269 if (line == vis) { return lineN }
2270 return lineNo(vis)
2271 }
2272
2273 // Get the line number of the start of the next visual line after
2274 // the given line.
2275 function visualLineEndNo(doc, lineN) {
2276 if (lineN > doc.lastLine()) { return lineN }
2277 var line = getLine(doc, lineN), merged;
2278 if (!lineIsHidden(doc, line)) { return lineN }
2279 while (merged = collapsedSpanAtEnd(line))
2280 { line = merged.find(1, true).line; }
2281 return lineNo(line) + 1
2282 }
2283
2284 // Compute whether a line is hidden. Lines count as hidden when they
2285 // are part of a visual line that starts with another line, or when
2286 // they are entirely covered by collapsed, non-widget span.
2287 function lineIsHidden(doc, line) {
2288 var sps = sawCollapsedSpans && line.markedSpans;
2289 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2290 sp = sps[i];
2291 if (!sp.marker.collapsed) { continue }
2292 if (sp.from == null) { return true }
2293 if (sp.marker.widgetNode) { continue }
2294 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
2295 { return true }
2296 } }
2297 }
2298 function lineIsHiddenInner(doc, line, span) {
2299 if (span.to == null) {
2300 var end = span.marker.find(1, true);
2301 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
2302 }
2303 if (span.marker.inclusiveRight && span.to == line.text.length)
2304 { return true }
2305 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
2306 sp = line.markedSpans[i];
2307 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
2308 (sp.to == null || sp.to != span.from) &&
2309 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
2310 lineIsHiddenInner(doc, line, sp)) { return true }
2311 }
2312 }
2313
2314 // Find the height above the given line.
2315 function heightAtLine(lineObj) {
2316 lineObj = visualLine(lineObj);
2317
2318 var h = 0, chunk = lineObj.parent;
2319 for (var i = 0; i < chunk.lines.length; ++i) {
2320 var line = chunk.lines[i];
2321 if (line == lineObj) { break }
2322 else { h += line.height; }
2323 }
2324 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
2325 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
2326 var cur = p.children[i$1];
2327 if (cur == chunk) { break }
2328 else { h += cur.height; }
2329 }
2330 }
2331 return h
2332 }
2333
2334 // Compute the character length of a line, taking into account
2335 // collapsed ranges (see markText) that might hide parts, and join
2336 // other lines onto it.
2337 function lineLength(line) {
2338 if (line.height == 0) { return 0 }
2339 var len = line.text.length, merged, cur = line;
2340 while (merged = collapsedSpanAtStart(cur)) {
2341 var found = merged.find(0, true);
2342 cur = found.from.line;
2343 len += found.from.ch - found.to.ch;
2344 }
2345 cur = line;
2346 while (merged = collapsedSpanAtEnd(cur)) {
2347 var found$1 = merged.find(0, true);
2348 len -= cur.text.length - found$1.from.ch;
2349 cur = found$1.to.line;
2350 len += cur.text.length - found$1.to.ch;
2351 }
2352 return len
2353 }
2354
2355 // Find the longest line in the document.
2356 function findMaxLine(cm) {
2357 var d = cm.display, doc = cm.doc;
2358 d.maxLine = getLine(doc, doc.first);
2359 d.maxLineLength = lineLength(d.maxLine);
2360 d.maxLineChanged = true;
2361 doc.iter(function (line) {
2362 var len = lineLength(line);
2363 if (len > d.maxLineLength) {
2364 d.maxLineLength = len;
2365 d.maxLine = line;
2366 }
2367 });
2368 }
2369
2370 // BIDI HELPERS
2371
2372 function iterateBidiSections(order, from, to, f) {
2373 if (!order) { return f(from, to, "ltr", 0) }
2374 var found = false;
2375 for (var i = 0; i < order.length; ++i) {
2376 var part = order[i];
2377 if (part.from < to && part.to > from || from == to && part.to == from) {
2378 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
2379 found = true;
2380 }
2381 }
2382 if (!found) { f(from, to, "ltr"); }
2383 }
2384
2385 var bidiOther = null;
2386 function getBidiPartAt(order, ch, sticky) {
2387 var found;
2388 bidiOther = null;
2389 for (var i = 0; i < order.length; ++i) {
2390 var cur = order[i];
2391 if (cur.from < ch && cur.to > ch) { return i }
2392 if (cur.to == ch) {
2393 if (cur.from != cur.to && sticky == "before") { found = i; }
2394 else { bidiOther = i; }
2395 }
2396 if (cur.from == ch) {
2397 if (cur.from != cur.to && sticky != "before") { found = i; }
2398 else { bidiOther = i; }
2399 }
2400 }
2401 return found != null ? found : bidiOther
2402 }
2403
2404 // Bidirectional ordering algorithm
2405 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
2406 // that this (partially) implements.
2407
2408 // One-char codes used for character types:
2409 // L (L): Left-to-Right
2410 // R (R): Right-to-Left
2411 // r (AL): Right-to-Left Arabic
2412 // 1 (EN): European Number
2413 // + (ES): European Number Separator
2414 // % (ET): European Number Terminator
2415 // n (AN): Arabic Number
2416 // , (CS): Common Number Separator
2417 // m (NSM): Non-Spacing Mark
2418 // b (BN): Boundary Neutral
2419 // s (B): Paragraph Separator
2420 // t (S): Segment Separator
2421 // w (WS): Whitespace
2422 // N (ON): Other Neutrals
2423
2424 // Returns null if characters are ordered as they appear
2425 // (left-to-right), or an array of sections ({from, to, level}
2426 // objects) in the order in which they occur visually.
2427 var bidiOrdering = (function() {
2428 // Character types for codepoints 0 to 0xff
2429 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
2430 // Character types for codepoints 0x600 to 0x6f9
2431 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
2432 function charType(code) {
2433 if (code <= 0xf7) { return lowTypes.charAt(code) }
2434 else if (0x590 <= code && code <= 0x5f4) { return "R" }
2435 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
2436 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
2437 else if (0x2000 <= code && code <= 0x200b) { return "w" }
2438 else if (code == 0x200c) { return "b" }
2439 else { return "L" }
2440 }
2441
2442 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2443 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
2444
2445 function BidiSpan(level, from, to) {
2446 this.level = level;
2447 this.from = from; this.to = to;
2448 }
2449
2450 return function(str, direction) {
2451 var outerType = direction == "ltr" ? "L" : "R";
2452
2453 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
2454 var len = str.length, types = [];
2455 for (var i = 0; i < len; ++i)
2456 { types.push(charType(str.charCodeAt(i))); }
2457
2458 // W1. Examine each non-spacing mark (NSM) in the level run, and
2459 // change the type of the NSM to the type of the previous
2460 // character. If the NSM is at the start of the level run, it will
2461 // get the type of sor.
2462 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2463 var type = types[i$1];
2464 if (type == "m") { types[i$1] = prev; }
2465 else { prev = type; }
2466 }
2467
2468 // W2. Search backwards from each instance of a European number
2469 // until the first strong type (R, L, AL, or sor) is found. If an
2470 // AL is found, change the type of the European number to Arabic
2471 // number.
2472 // W3. Change all ALs to R.
2473 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2474 var type$1 = types[i$2];
2475 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2476 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2477 }
2478
2479 // W4. A single European separator between two European numbers
2480 // changes to a European number. A single common separator between
2481 // two numbers of the same type changes to that type.
2482 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2483 var type$2 = types[i$3];
2484 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2485 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2486 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2487 prev$1 = type$2;
2488 }
2489
2490 // W5. A sequence of European terminators adjacent to European
2491 // numbers changes to all European numbers.
2492 // W6. Otherwise, separators and terminators change to Other
2493 // Neutral.
2494 for (var i$4 = 0; i$4 < len; ++i$4) {
2495 var type$3 = types[i$4];
2496 if (type$3 == ",") { types[i$4] = "N"; }
2497 else if (type$3 == "%") {
2498 var end = (void 0);
2499 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2500 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2501 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2502 i$4 = end - 1;
2503 }
2504 }
2505
2506 // W7. Search backwards from each instance of a European number
2507 // until the first strong type (R, L, or sor) is found. If an L is
2508 // found, then change the type of the European number to L.
2509 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2510 var type$4 = types[i$5];
2511 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2512 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2513 }
2514
2515 // N1. A sequence of neutrals takes the direction of the
2516 // surrounding strong text if the text on both sides has the same
2517 // direction. European and Arabic numbers act as if they were R in
2518 // terms of their influence on neutrals. Start-of-level-run (sor)
2519 // and end-of-level-run (eor) are used at level run boundaries.
2520 // N2. Any remaining neutrals take the embedding direction.
2521 for (var i$6 = 0; i$6 < len; ++i$6) {
2522 if (isNeutral.test(types[i$6])) {
2523 var end$1 = (void 0);
2524 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2525 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2526 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2527 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2528 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2529 i$6 = end$1 - 1;
2530 }
2531 }
2532
2533 // Here we depart from the documented algorithm, in order to avoid
2534 // building up an actual levels array. Since there are only three
2535 // levels (0, 1, 2) in an implementation that doesn't take
2536 // explicit embedding into account, we can build up the order on
2537 // the fly, without following the level-based algorithm.
2538 var order = [], m;
2539 for (var i$7 = 0; i$7 < len;) {
2540 if (countsAsLeft.test(types[i$7])) {
2541 var start = i$7;
2542 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2543 order.push(new BidiSpan(0, start, i$7));
2544 } else {
2545 var pos = i$7, at = order.length;
2546 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2547 for (var j$2 = pos; j$2 < i$7;) {
2548 if (countsAsNum.test(types[j$2])) {
2549 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2550 var nstart = j$2;
2551 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2552 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2553 pos = j$2;
2554 } else { ++j$2; }
2555 }
2556 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2557 }
2558 }
2559 if (direction == "ltr") {
2560 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2561 order[0].from = m[0].length;
2562 order.unshift(new BidiSpan(0, 0, m[0].length));
2563 }
2564 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2565 lst(order).to -= m[0].length;
2566 order.push(new BidiSpan(0, len - m[0].length, len));
2567 }
2568 }
2569
2570 return direction == "rtl" ? order.reverse() : order
2571 }
2572 })();
2573
2574 // Get the bidi ordering for the given line (and cache it). Returns
2575 // false for lines that are fully left-to-right, and an array of
2576 // BidiSpan objects otherwise.
2577 function getOrder(line, direction) {
2578 var order = line.order;
2579 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2580 return order
2581 }
2582
2583 // EVENT HANDLING
2584
2585 // Lightweight event framework. on/off also work on DOM nodes,
2586 // registering native DOM handlers.
2587
2588 var noHandlers = [];
2589
2590 var on = function(emitter, type, f) {
2591 if (emitter.addEventListener) {
2592 emitter.addEventListener(type, f, false);
2593 } else if (emitter.attachEvent) {
2594 emitter.attachEvent("on" + type, f);
2595 } else {
2596 var map$$1 = emitter._handlers || (emitter._handlers = {});
2597 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2598 }
2599 };
2600
2601 function getHandlers(emitter, type) {
2602 return emitter._handlers && emitter._handlers[type] || noHandlers
2603 }
2604
2605 function off(emitter, type, f) {
2606 if (emitter.removeEventListener) {
2607 emitter.removeEventListener(type, f, false);
2608 } else if (emitter.detachEvent) {
2609 emitter.detachEvent("on" + type, f);
2610 } else {
2611 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2612 if (arr) {
2613 var index = indexOf(arr, f);
2614 if (index > -1)
2615 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2616 }
2617 }
2618 }
2619
2620 function signal(emitter, type /*, values...*/) {
2621 var handlers = getHandlers(emitter, type);
2622 if (!handlers.length) { return }
2623 var args = Array.prototype.slice.call(arguments, 2);
2624 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2625 }
2626
2627 // The DOM events that CodeMirror handles can be overridden by
2628 // registering a (non-DOM) handler on the editor for the event name,
2629 // and preventDefault-ing the event in that handler.
2630 function signalDOMEvent(cm, e, override) {
2631 if (typeof e == "string")
2632 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2633 signal(cm, override || e.type, cm, e);
2634 return e_defaultPrevented(e) || e.codemirrorIgnore
2635 }
2636
2637 function signalCursorActivity(cm) {
2638 var arr = cm._handlers && cm._handlers.cursorActivity;
2639 if (!arr) { return }
2640 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2641 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2642 { set.push(arr[i]); } }
2643 }
2644
2645 function hasHandler(emitter, type) {
2646 return getHandlers(emitter, type).length > 0
2647 }
2648
2649 // Add on and off methods to a constructor's prototype, to make
2650 // registering events on such objects more convenient.
2651 function eventMixin(ctor) {
2652 ctor.prototype.on = function(type, f) {on(this, type, f);};
2653 ctor.prototype.off = function(type, f) {off(this, type, f);};
2654 }
2655
2656 // Due to the fact that we still support jurassic IE versions, some
2657 // compatibility wrappers are needed.
2658
2659 function e_preventDefault(e) {
2660 if (e.preventDefault) { e.preventDefault(); }
2661 else { e.returnValue = false; }
2662 }
2663 function e_stopPropagation(e) {
2664 if (e.stopPropagation) { e.stopPropagation(); }
2665 else { e.cancelBubble = true; }
2666 }
2667 function e_defaultPrevented(e) {
2668 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2669 }
2670 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2671
2672 function e_target(e) {return e.target || e.srcElement}
2673 function e_button(e) {
2674 var b = e.which;
2675 if (b == null) {
2676 if (e.button & 1) { b = 1; }
2677 else if (e.button & 2) { b = 3; }
2678 else if (e.button & 4) { b = 2; }
2679 }
2680 if (mac && e.ctrlKey && b == 1) { b = 3; }
2681 return b
2682 }
2683
2684 // Detect drag-and-drop
2685 var dragAndDrop = function() {
2686 // There is *some* kind of drag-and-drop support in IE6-8, but I
2687 // couldn't get it to work yet.
2688 if (ie && ie_version < 9) { return false }
2689 var div = elt('div');
2690 return "draggable" in div || "dragDrop" in div
2691 }();
2692
2693 var zwspSupported;
2694 function zeroWidthElement(measure) {
2695 if (zwspSupported == null) {
2696 var test = elt("span", "\u200b");
2697 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2698 if (measure.firstChild.offsetHeight != 0)
2699 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2700 }
2701 var node = zwspSupported ? elt("span", "\u200b") :
2702 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2703 node.setAttribute("cm-text", "");
2704 return node
2705 }
2706
2707 // Feature-detect IE's crummy client rect reporting for bidi text
2708 var badBidiRects;
2709 function hasBadBidiRects(measure) {
2710 if (badBidiRects != null) { return badBidiRects }
2711 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2712 var r0 = range(txt, 0, 1).getBoundingClientRect();
2713 var r1 = range(txt, 1, 2).getBoundingClientRect();
2714 removeChildren(measure);
2715 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2716 return badBidiRects = (r1.right - r0.right < 3)
2717 }
2718
2719 // See if "".split is the broken IE version, if so, provide an
2720 // alternative way to split lines.
2721 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2722 var pos = 0, result = [], l = string.length;
2723 while (pos <= l) {
2724 var nl = string.indexOf("\n", pos);
2725 if (nl == -1) { nl = string.length; }
2726 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2727 var rt = line.indexOf("\r");
2728 if (rt != -1) {
2729 result.push(line.slice(0, rt));
2730 pos += rt + 1;
2731 } else {
2732 result.push(line);
2733 pos = nl + 1;
2734 }
2735 }
2736 return result
2737 } : function (string) { return string.split(/\r\n?|\n/); };
2738
2739 var hasSelection = window.getSelection ? function (te) {
2740 try { return te.selectionStart != te.selectionEnd }
2741 catch(e) { return false }
2742 } : function (te) {
2743 var range$$1;
2744 try {range$$1 = te.ownerDocument.selection.createRange();}
2745 catch(e) {}
2746 if (!range$$1 || range$$1.parentElement() != te) { return false }
2747 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2748 };
2749
2750 var hasCopyEvent = (function () {
2751 var e = elt("div");
2752 if ("oncopy" in e) { return true }
2753 e.setAttribute("oncopy", "return;");
2754 return typeof e.oncopy == "function"
2755 })();
2756
2757 var badZoomedRects = null;
2758 function hasBadZoomedRects(measure) {
2759 if (badZoomedRects != null) { return badZoomedRects }
2760 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2761 var normal = node.getBoundingClientRect();
2762 var fromRange = range(node, 0, 1).getBoundingClientRect();
2763 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2764 }
2765
2766 // Known modes, by name and by MIME
2767 var modes = {};
2768 var mimeModes = {};
2769
2770 // Extra arguments are stored as the mode's dependencies, which is
2771 // used by (legacy) mechanisms like loadmode.js to automatically
2772 // load a mode. (Preferred mechanism is the require/define calls.)
2773 function defineMode(name, mode) {
2774 if (arguments.length > 2)
2775 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2776 modes[name] = mode;
2777 }
2778
2779 function defineMIME(mime, spec) {
2780 mimeModes[mime] = spec;
2781 }
2782
2783 // Given a MIME type, a {name, ...options} config object, or a name
2784 // string, return a mode config object.
2785 function resolveMode(spec) {
2786 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2787 spec = mimeModes[spec];
2788 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2789 var found = mimeModes[spec.name];
2790 if (typeof found == "string") { found = {name: found}; }
2791 spec = createObj(found, spec);
2792 spec.name = found.name;
2793 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2794 return resolveMode("application/xml")
2795 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2796 return resolveMode("application/json")
2797 }
2798 if (typeof spec == "string") { return {name: spec} }
2799 else { return spec || {name: "null"} }
2800 }
2801
2802 // Given a mode spec (anything that resolveMode accepts), find and
2803 // initialize an actual mode object.
2804 function getMode(options, spec) {
2805 spec = resolveMode(spec);
2806 var mfactory = modes[spec.name];
2807 if (!mfactory) { return getMode(options, "text/plain") }
2808 var modeObj = mfactory(options, spec);
2809 if (modeExtensions.hasOwnProperty(spec.name)) {
2810 var exts = modeExtensions[spec.name];
2811 for (var prop in exts) {
2812 if (!exts.hasOwnProperty(prop)) { continue }
2813 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2814 modeObj[prop] = exts[prop];
2815 }
2816 }
2817 modeObj.name = spec.name;
2818 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2819 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2820 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2821
2822 return modeObj
2823 }
2824
2825 // This can be used to attach properties to mode objects from
2826 // outside the actual mode definition.
2827 var modeExtensions = {};
2828 function extendMode(mode, properties) {
2829 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2830 copyObj(properties, exts);
2831 }
2832
2833 function copyState(mode, state) {
2834 if (state === true) { return state }
2835 if (mode.copyState) { return mode.copyState(state) }
2836 var nstate = {};
2837 for (var n in state) {
2838 var val = state[n];
2839 if (val instanceof Array) { val = val.concat([]); }
2840 nstate[n] = val;
2841 }
2842 return nstate
2843 }
2844
2845 // Given a mode and a state (for that mode), find the inner mode and
2846 // state at the position that the state refers to.
2847 function innerMode(mode, state) {
2848 var info;
2849 while (mode.innerMode) {
2850 info = mode.innerMode(state);
2851 if (!info || info.mode == mode) { break }
2852 state = info.state;
2853 mode = info.mode;
2854 }
2855 return info || {mode: mode, state: state}
2856 }
2857
2858 function startState(mode, a1, a2) {
2859 return mode.startState ? mode.startState(a1, a2) : true
2860 }
2861
2862 // STRING STREAM
2863
2864 // Fed to the mode parsers, provides helper functions to make
2865 // parsers more succinct.
2866
2867 var StringStream = function(string, tabSize, lineOracle) {
2868 this.pos = this.start = 0;
2869 this.string = string;
2870 this.tabSize = tabSize || 8;
2871 this.lastColumnPos = this.lastColumnValue = 0;
2872 this.lineStart = 0;
2873 this.lineOracle = lineOracle;
2874 };
2875
2876 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
2877 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
2878 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
2879 StringStream.prototype.next = function () {
2880 if (this.pos < this.string.length)
2881 { return this.string.charAt(this.pos++) }
2882 };
2883 StringStream.prototype.eat = function (match) {
2884 var ch = this.string.charAt(this.pos);
2885 var ok;
2886 if (typeof match == "string") { ok = ch == match; }
2887 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
2888 if (ok) {++this.pos; return ch}
2889 };
2890 StringStream.prototype.eatWhile = function (match) {
2891 var start = this.pos;
2892 while (this.eat(match)){}
2893 return this.pos > start
2894 };
2895 StringStream.prototype.eatSpace = function () {
2896 var this$1 = this;
2897
2898 var start = this.pos;
2899 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
2900 return this.pos > start
2901 };
2902 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
2903 StringStream.prototype.skipTo = function (ch) {
2904 var found = this.string.indexOf(ch, this.pos);
2905 if (found > -1) {this.pos = found; return true}
2906 };
2907 StringStream.prototype.backUp = function (n) {this.pos -= n;};
2908 StringStream.prototype.column = function () {
2909 if (this.lastColumnPos < this.start) {
2910 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
2911 this.lastColumnPos = this.start;
2912 }
2913 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2914 };
2915 StringStream.prototype.indentation = function () {
2916 return countColumn(this.string, null, this.tabSize) -
2917 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2918 };
2919 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
2920 if (typeof pattern == "string") {
2921 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
2922 var substr = this.string.substr(this.pos, pattern.length);
2923 if (cased(substr) == cased(pattern)) {
2924 if (consume !== false) { this.pos += pattern.length; }
2925 return true
2926 }
2927 } else {
2928 var match = this.string.slice(this.pos).match(pattern);
2929 if (match && match.index > 0) { return null }
2930 if (match && consume !== false) { this.pos += match[0].length; }
2931 return match
2932 }
2933 };
2934 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
2935 StringStream.prototype.hideFirstChars = function (n, inner) {
2936 this.lineStart += n;
2937 try { return inner() }
2938 finally { this.lineStart -= n; }
2939 };
2940 StringStream.prototype.lookAhead = function (n) {
2941 var oracle = this.lineOracle;
2942 return oracle && oracle.lookAhead(n)
2943 };
2944 StringStream.prototype.baseToken = function () {
2945 var oracle = this.lineOracle;
2946 return oracle && oracle.baseToken(this.pos)
2947 };
2948
2949 var SavedContext = function(state, lookAhead) {
2950 this.state = state;
2951 this.lookAhead = lookAhead;
2952 };
2953
2954 var Context = function(doc, state, line, lookAhead) {
2955 this.state = state;
2956 this.doc = doc;
2957 this.line = line;
2958 this.maxLookAhead = lookAhead || 0;
2959 this.baseTokens = null;
2960 this.baseTokenPos = 1;
2961 };
2962
2963 Context.prototype.lookAhead = function (n) {
2964 var line = this.doc.getLine(this.line + n);
2965 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
2966 return line
2967 };
2968
2969 Context.prototype.baseToken = function (n) {
2970 var this$1 = this;
2971
2972 if (!this.baseTokens) { return null }
2973 while (this.baseTokens[this.baseTokenPos] <= n)
2974 { this$1.baseTokenPos += 2; }
2975 var type = this.baseTokens[this.baseTokenPos + 1];
2976 return {type: type && type.replace(/( |^)overlay .*/, ""),
2977 size: this.baseTokens[this.baseTokenPos] - n}
2978 };
2979
2980 Context.prototype.nextLine = function () {
2981 this.line++;
2982 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
2983 };
2984
2985 Context.fromSaved = function (doc, saved, line) {
2986 if (saved instanceof SavedContext)
2987 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
2988 else
2989 { return new Context(doc, copyState(doc.mode, saved), line) }
2990 };
2991
2992 Context.prototype.save = function (copy) {
2993 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
2994 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
2995 };
2996
2997
2998 // Compute a style array (an array starting with a mode generation
2999 // -- for invalidation -- followed by pairs of end positions and
3000 // style strings), which is used to highlight the tokens on the
3001 // line.
3002 function highlightLine(cm, line, context, forceToEnd) {
3003 // A styles array always starts with a number identifying the
3004 // mode/overlays that it is based on (for easy invalidation).
3005 var st = [cm.state.modeGen], lineClasses = {};
3006 // Compute the base array of styles
3007 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
3008 lineClasses, forceToEnd);
3009 var state = context.state;
3010
3011 // Run overlays, adjust style array.
3012 var loop = function ( o ) {
3013 context.baseTokens = st;
3014 var overlay = cm.state.overlays[o], i = 1, at = 0;
3015 context.state = true;
3016 runMode(cm, line.text, overlay.mode, context, function (end, style) {
3017 var start = i;
3018 // Ensure there's a token end at the current position, and that i points at it
3019 while (at < end) {
3020 var i_end = st[i];
3021 if (i_end > end)
3022 { st.splice(i, 1, end, st[i+1], i_end); }
3023 i += 2;
3024 at = Math.min(end, i_end);
3025 }
3026 if (!style) { return }
3027 if (overlay.opaque) {
3028 st.splice(start, i - start, end, "overlay " + style);
3029 i = start + 2;
3030 } else {
3031 for (; start < i; start += 2) {
3032 var cur = st[start+1];
3033 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
3034 }
3035 }
3036 }, lineClasses);
3037 context.state = state;
3038 context.baseTokens = null;
3039 context.baseTokenPos = 1;
3040 };
3041
3042 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
3043
3044 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
3045 }
3046
3047 function getLineStyles(cm, line, updateFrontier) {
3048 if (!line.styles || line.styles[0] != cm.state.modeGen) {
3049 var context = getContextBefore(cm, lineNo(line));
3050 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
3051 var result = highlightLine(cm, line, context);
3052 if (resetState) { context.state = resetState; }
3053 line.stateAfter = context.save(!resetState);
3054 line.styles = result.styles;
3055 if (result.classes) { line.styleClasses = result.classes; }
3056 else if (line.styleClasses) { line.styleClasses = null; }
3057 if (updateFrontier === cm.doc.highlightFrontier)
3058 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
3059 }
3060 return line.styles
3061 }
3062
3063 function getContextBefore(cm, n, precise) {
3064 var doc = cm.doc, display = cm.display;
3065 if (!doc.mode.startState) { return new Context(doc, true, n) }
3066 var start = findStartLine(cm, n, precise);
3067 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
3068 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
3069
3070 doc.iter(start, n, function (line) {
3071 processLine(cm, line.text, context);
3072 var pos = context.line;
3073 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
3074 context.nextLine();
3075 });
3076 if (precise) { doc.modeFrontier = context.line; }
3077 return context
3078 }
3079
3080 // Lightweight form of highlight -- proceed over this line and
3081 // update state, but don't save a style array. Used for lines that
3082 // aren't currently visible.
3083 function processLine(cm, text, context, startAt) {
3084 var mode = cm.doc.mode;
3085 var stream = new StringStream(text, cm.options.tabSize, context);
3086 stream.start = stream.pos = startAt || 0;
3087 if (text == "") { callBlankLine(mode, context.state); }
3088 while (!stream.eol()) {
3089 readToken(mode, stream, context.state);
3090 stream.start = stream.pos;
3091 }
3092 }
3093
3094 function callBlankLine(mode, state) {
3095 if (mode.blankLine) { return mode.blankLine(state) }
3096 if (!mode.innerMode) { return }
3097 var inner = innerMode(mode, state);
3098 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
3099 }
3100
3101 function readToken(mode, stream, state, inner) {
3102 for (var i = 0; i < 10; i++) {
3103 if (inner) { inner[0] = innerMode(mode, state).mode; }
3104 var style = mode.token(stream, state);
3105 if (stream.pos > stream.start) { return style }
3106 }
3107 throw new Error("Mode " + mode.name + " failed to advance stream.")
3108 }
3109
3110 var Token = function(stream, type, state) {
3111 this.start = stream.start; this.end = stream.pos;
3112 this.string = stream.current();
3113 this.type = type || null;
3114 this.state = state;
3115 };
3116
3117 // Utility for getTokenAt and getLineTokens
3118 function takeToken(cm, pos, precise, asArray) {
3119 var doc = cm.doc, mode = doc.mode, style;
3120 pos = clipPos(doc, pos);
3121 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
3122 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
3123 if (asArray) { tokens = []; }
3124 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
3125 stream.start = stream.pos;
3126 style = readToken(mode, stream, context.state);
3127 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
3128 }
3129 return asArray ? tokens : new Token(stream, style, context.state)
3130 }
3131
3132 function extractLineClasses(type, output) {
3133 if (type) { for (;;) {
3134 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
3135 if (!lineClass) { break }
3136 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
3137 var prop = lineClass[1] ? "bgClass" : "textClass";
3138 if (output[prop] == null)
3139 { output[prop] = lineClass[2]; }
3140 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
3141 { output[prop] += " " + lineClass[2]; }
3142 } }
3143 return type
3144 }
3145
3146 // Run the given mode's parser over a line, calling f for each token.
3147 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
3148 var flattenSpans = mode.flattenSpans;
3149 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
3150 var curStart = 0, curStyle = null;
3151 var stream = new StringStream(text, cm.options.tabSize, context), style;
3152 var inner = cm.options.addModeClass && [null];
3153 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
3154 while (!stream.eol()) {
3155 if (stream.pos > cm.options.maxHighlightLength) {
3156 flattenSpans = false;
3157 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
3158 stream.pos = text.length;
3159 style = null;
3160 } else {
3161 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
3162 }
3163 if (inner) {
3164 var mName = inner[0].name;
3165 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
3166 }
3167 if (!flattenSpans || curStyle != style) {
3168 while (curStart < stream.start) {
3169 curStart = Math.min(stream.start, curStart + 5000);
3170 f(curStart, curStyle);
3171 }
3172 curStyle = style;
3173 }
3174 stream.start = stream.pos;
3175 }
3176 while (curStart < stream.pos) {
3177 // Webkit seems to refuse to render text nodes longer than 57444
3178 // characters, and returns inaccurate measurements in nodes
3179 // starting around 5000 chars.
3180 var pos = Math.min(stream.pos, curStart + 5000);
3181 f(pos, curStyle);
3182 curStart = pos;
3183 }
3184 }
3185
3186 // Finds the line to start with when starting a parse. Tries to
3187 // find a line with a stateAfter, so that it can start with a
3188 // valid state. If that fails, it returns the line with the
3189 // smallest indentation, which tends to need the least context to
3190 // parse correctly.
3191 function findStartLine(cm, n, precise) {
3192 var minindent, minline, doc = cm.doc;
3193 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
3194 for (var search = n; search > lim; --search) {
3195 if (search <= doc.first) { return doc.first }
3196 var line = getLine(doc, search - 1), after = line.stateAfter;
3197 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
3198 { return search }
3199 var indented = countColumn(line.text, null, cm.options.tabSize);
3200 if (minline == null || minindent > indented) {
3201 minline = search - 1;
3202 minindent = indented;
3203 }
3204 }
3205 return minline
3206 }
3207
3208 function retreatFrontier(doc, n) {
3209 doc.modeFrontier = Math.min(doc.modeFrontier, n);
3210 if (doc.highlightFrontier < n - 10) { return }
3211 var start = doc.first;
3212 for (var line = n - 1; line > start; line--) {
3213 var saved = getLine(doc, line).stateAfter;
3214 // change is on 3
3215 // state on line 1 looked ahead 2 -- so saw 3
3216 // test 1 + 2 < 3 should cover this
3217 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
3218 start = line + 1;
3219 break
3220 }
3221 }
3222 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
3223 }
3224
3225 // LINE DATA STRUCTURE
3226
3227 // Line objects. These hold state related to a line, including
3228 // highlighting info (the styles array).
3229 var Line = function(text, markedSpans, estimateHeight) {
3230 this.text = text;
3231 attachMarkedSpans(this, markedSpans);
3232 this.height = estimateHeight ? estimateHeight(this) : 1;
3233 };
3234
3235 Line.prototype.lineNo = function () { return lineNo(this) };
3236 eventMixin(Line);
3237
3238 // Change the content (text, markers) of a line. Automatically
3239 // invalidates cached information and tries to re-estimate the
3240 // line's height.
3241 function updateLine(line, text, markedSpans, estimateHeight) {
3242 line.text = text;
3243 if (line.stateAfter) { line.stateAfter = null; }
3244 if (line.styles) { line.styles = null; }
3245 if (line.order != null) { line.order = null; }
3246 detachMarkedSpans(line);
3247 attachMarkedSpans(line, markedSpans);
3248 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3249 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3250 }
3251
3252 // Detach a line from the document tree and its markers.
3253 function cleanUpLine(line) {
3254 line.parent = null;
3255 detachMarkedSpans(line);
3256 }
3257
3258 // Convert a style as returned by a mode (either null, or a string
3259 // containing one or more styles) to a CSS style. This is cached,
3260 // and also looks for line-wide styles.
3261 var styleToClassCache = {};
3262 var styleToClassCacheWithMode = {};
3263 function interpretTokenStyle(style, options) {
3264 if (!style || /^\s*$/.test(style)) { return null }
3265 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3266 return cache[style] ||
3267 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3268 }
3269
3270 // Render the DOM representation of the text of a line. Also builds
3271 // up a 'line map', which points at the DOM nodes that represent
3272 // specific stretches of text, and is used by the measuring code.
3273 // The returned object contains the DOM node, this map, and
3274 // information about line-wide styles that were set by the mode.
3275 function buildLineContent(cm, lineView) {
3276 // The padding-right forces the element to have a 'border', which
3277 // is needed on Webkit to be able to get line-level bounding
3278 // rectangles for it (in measureChar).
3279 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3280 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3281 col: 0, pos: 0, cm: cm,
3282 trailingSpace: false,
3283 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
3284 lineView.measure = {};
3285
3286 // Iterate over the logical lines that make up this visual line.
3287 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3288 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3289 builder.pos = 0;
3290 builder.addToken = buildToken;
3291 // Optionally wire in some hacks into the token-rendering
3292 // algorithm, to deal with browser quirks.
3293 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3294 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3295 builder.map = [];
3296 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3297 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3298 if (line.styleClasses) {
3299 if (line.styleClasses.bgClass)
3300 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3301 if (line.styleClasses.textClass)
3302 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3303 }
3304
3305 // Ensure at least a single node is present, for measuring.
3306 if (builder.map.length == 0)
3307 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3308
3309 // Store the map and a cache object for the current logical line
3310 if (i == 0) {
3311 lineView.measure.map = builder.map;
3312 lineView.measure.cache = {};
3313 } else {
3314 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3315 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3316 }
3317 }
3318
3319 // See issue #2901
3320 if (webkit) {
3321 var last = builder.content.lastChild;
3322 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3323 { builder.content.className = "cm-tab-wrap-hack"; }
3324 }
3325
3326 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3327 if (builder.pre.className)
3328 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3329
3330 return builder
3331 }
3332
3333 function defaultSpecialCharPlaceholder(ch) {
3334 var token = elt("span", "\u2022", "cm-invalidchar");
3335 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3336 token.setAttribute("aria-label", token.title);
3337 return token
3338 }
3339
3340 // Build up the DOM representation for a single token, and add it to
3341 // the line map. Takes care to render special characters separately.
3342 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
3343 if (!text) { return }
3344 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3345 var special = builder.cm.state.specialChars, mustWrap = false;
3346 var content;
3347 if (!special.test(text)) {
3348 builder.col += text.length;
3349 content = document.createTextNode(displayText);
3350 builder.map.push(builder.pos, builder.pos + text.length, content);
3351 if (ie && ie_version < 9) { mustWrap = true; }
3352 builder.pos += text.length;
3353 } else {
3354 content = document.createDocumentFragment();
3355 var pos = 0;
3356 while (true) {
3357 special.lastIndex = pos;
3358 var m = special.exec(text);
3359 var skipped = m ? m.index - pos : text.length - pos;
3360 if (skipped) {
3361 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3362 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3363 else { content.appendChild(txt); }
3364 builder.map.push(builder.pos, builder.pos + skipped, txt);
3365 builder.col += skipped;
3366 builder.pos += skipped;
3367 }
3368 if (!m) { break }
3369 pos += skipped + 1;
3370 var txt$1 = (void 0);
3371 if (m[0] == "\t") {
3372 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3373 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3374 txt$1.setAttribute("role", "presentation");
3375 txt$1.setAttribute("cm-text", "\t");
3376 builder.col += tabWidth;
3377 } else if (m[0] == "\r" || m[0] == "\n") {
3378 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3379 txt$1.setAttribute("cm-text", m[0]);
3380 builder.col += 1;
3381 } else {
3382 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3383 txt$1.setAttribute("cm-text", m[0]);
3384 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3385 else { content.appendChild(txt$1); }
3386 builder.col += 1;
3387 }
3388 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3389 builder.pos++;
3390 }
3391 }
3392 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3393 if (style || startStyle || endStyle || mustWrap || css) {
3394 var fullStyle = style || "";
3395 if (startStyle) { fullStyle += startStyle; }
3396 if (endStyle) { fullStyle += endStyle; }
3397 var token = elt("span", [content], fullStyle, css);
3398 if (title) { token.title = title; }
3399 return builder.content.appendChild(token)
3400 }
3401 builder.content.appendChild(content);
3402 }
3403
3404 function splitSpaces(text, trailingBefore) {
3405 if (text.length > 1 && !/ /.test(text)) { return text }
3406 var spaceBefore = trailingBefore, result = "";
3407 for (var i = 0; i < text.length; i++) {
3408 var ch = text.charAt(i);
3409 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3410 { ch = "\u00a0"; }
3411 result += ch;
3412 spaceBefore = ch == " ";
3413 }
3414 return result
3415 }
3416
3417 // Work around nonsense dimensions being reported for stretches of
3418 // right-to-left text.
3419 function buildTokenBadBidi(inner, order) {
3420 return function (builder, text, style, startStyle, endStyle, title, css) {
3421 style = style ? style + " cm-force-border" : "cm-force-border";
3422 var start = builder.pos, end = start + text.length;
3423 for (;;) {
3424 // Find the part that overlaps with the start of this text
3425 var part = (void 0);
3426 for (var i = 0; i < order.length; i++) {
3427 part = order[i];
3428 if (part.to > start && part.from <= start) { break }
3429 }
3430 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
3431 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
3432 startStyle = null;
3433 text = text.slice(part.to - start);
3434 start = part.to;
3435 }
3436 }
3437 }
3438
3439 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3440 var widget = !ignoreWidget && marker.widgetNode;
3441 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3442 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3443 if (!widget)
3444 { widget = builder.content.appendChild(document.createElement("span")); }
3445 widget.setAttribute("cm-marker", marker.id);
3446 }
3447 if (widget) {
3448 builder.cm.display.input.setUneditable(widget);
3449 builder.content.appendChild(widget);
3450 }
3451 builder.pos += size;
3452 builder.trailingSpace = false;
3453 }
3454
3455 // Outputs a number of spans to make up a line, taking highlighting
3456 // and marked text into account.
3457 function insertLineContent(line, builder, styles) {
3458 var spans = line.markedSpans, allText = line.text, at = 0;
3459 if (!spans) {
3460 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3461 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3462 return
3463 }
3464
3465 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3466 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
3467 for (;;) {
3468 if (nextChange == pos) { // Update current marker set
3469 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
3470 collapsed = null; nextChange = Infinity;
3471 var foundBookmarks = [], endStyles = (void 0);
3472 for (var j = 0; j < spans.length; ++j) {
3473 var sp = spans[j], m = sp.marker;
3474 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3475 foundBookmarks.push(m);
3476 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3477 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3478 nextChange = sp.to;
3479 spanEndStyle = "";
3480 }
3481 if (m.className) { spanStyle += " " + m.className; }
3482 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3483 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3484 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3485 if (m.title && !title) { title = m.title; }
3486 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3487 { collapsed = sp; }
3488 } else if (sp.from > pos && nextChange > sp.from) {
3489 nextChange = sp.from;
3490 }
3491 }
3492 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3493 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3494
3495 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3496 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3497 if (collapsed && (collapsed.from || 0) == pos) {
3498 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3499 collapsed.marker, collapsed.from == null);
3500 if (collapsed.to == null) { return }
3501 if (collapsed.to == pos) { collapsed = false; }
3502 }
3503 }
3504 if (pos >= len) { break }
3505
3506 var upto = Math.min(len, nextChange);
3507 while (true) {
3508 if (text) {
3509 var end = pos + text.length;
3510 if (!collapsed) {
3511 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3512 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3513 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
3514 }
3515 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3516 pos = end;
3517 spanStartStyle = "";
3518 }
3519 text = allText.slice(at, at = styles[i++]);
3520 style = interpretTokenStyle(styles[i++], builder.cm.options);
3521 }
3522 }
3523 }
3524
3525
3526 // These objects are used to represent the visible (currently drawn)
3527 // part of the document. A LineView may correspond to multiple
3528 // logical lines, if those are connected by collapsed ranges.
3529 function LineView(doc, line, lineN) {
3530 // The starting line
3531 this.line = line;
3532 // Continuing lines, if any
3533 this.rest = visualLineContinued(line);
3534 // Number of logical lines in this visual line
3535 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3536 this.node = this.text = null;
3537 this.hidden = lineIsHidden(doc, line);
3538 }
3539
3540 // Create a range of LineView objects for the given lines.
3541 function buildViewArray(cm, from, to) {
3542 var array = [], nextPos;
3543 for (var pos = from; pos < to; pos = nextPos) {
3544 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3545 nextPos = pos + view.size;
3546 array.push(view);
3547 }
3548 return array
3549 }
3550
3551 var operationGroup = null;
3552
3553 function pushOperation(op) {
3554 if (operationGroup) {
3555 operationGroup.ops.push(op);
3556 } else {
3557 op.ownsGroup = operationGroup = {
3558 ops: [op],
3559 delayedCallbacks: []
3560 };
3561 }
3562 }
3563
3564 function fireCallbacksForOps(group) {
3565 // Calls delayed callbacks and cursorActivity handlers until no
3566 // new ones appear
3567 var callbacks = group.delayedCallbacks, i = 0;
3568 do {
3569 for (; i < callbacks.length; i++)
3570 { callbacks[i].call(null); }
3571 for (var j = 0; j < group.ops.length; j++) {
3572 var op = group.ops[j];
3573 if (op.cursorActivityHandlers)
3574 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3575 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3576 }
3577 } while (i < callbacks.length)
3578 }
3579
3580 function finishOperation(op, endCb) {
3581 var group = op.ownsGroup;
3582 if (!group) { return }
3583
3584 try { fireCallbacksForOps(group); }
3585 finally {
3586 operationGroup = null;
3587 endCb(group);
3588 }
3589 }
3590
3591 var orphanDelayedCallbacks = null;
3592
3593 // Often, we want to signal events at a point where we are in the
3594 // middle of some work, but don't want the handler to start calling
3595 // other methods on the editor, which might be in an inconsistent
3596 // state or simply not expect any other events to happen.
3597 // signalLater looks whether there are any handlers, and schedules
3598 // them to be executed when the last operation ends, or, if no
3599 // operation is active, when a timeout fires.
3600 function signalLater(emitter, type /*, values...*/) {
3601 var arr = getHandlers(emitter, type);
3602 if (!arr.length) { return }
3603 var args = Array.prototype.slice.call(arguments, 2), list;
3604 if (operationGroup) {
3605 list = operationGroup.delayedCallbacks;
3606 } else if (orphanDelayedCallbacks) {
3607 list = orphanDelayedCallbacks;
3608 } else {
3609 list = orphanDelayedCallbacks = [];
3610 setTimeout(fireOrphanDelayed, 0);
3611 }
3612 var loop = function ( i ) {
3613 list.push(function () { return arr[i].apply(null, args); });
3614 };
3615
3616 for (var i = 0; i < arr.length; ++i)
3617 loop( i );
3618 }
3619
3620 function fireOrphanDelayed() {
3621 var delayed = orphanDelayedCallbacks;
3622 orphanDelayedCallbacks = null;
3623 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3624 }
3625
3626 // When an aspect of a line changes, a string is added to
3627 // lineView.changes. This updates the relevant part of the line's
3628 // DOM structure.
3629 function updateLineForChanges(cm, lineView, lineN, dims) {
3630 for (var j = 0; j < lineView.changes.length; j++) {
3631 var type = lineView.changes[j];
3632 if (type == "text") { updateLineText(cm, lineView); }
3633 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3634 else if (type == "class") { updateLineClasses(cm, lineView); }
3635 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3636 }
3637 lineView.changes = null;
3638 }
3639
3640 // Lines with gutter elements, widgets or a background class need to
3641 // be wrapped, and have the extra elements added to the wrapper div
3642 function ensureLineWrapped(lineView) {
3643 if (lineView.node == lineView.text) {
3644 lineView.node = elt("div", null, null, "position: relative");
3645 if (lineView.text.parentNode)
3646 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3647 lineView.node.appendChild(lineView.text);
3648 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3649 }
3650 return lineView.node
3651 }
3652
3653 function updateLineBackground(cm, lineView) {
3654 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3655 if (cls) { cls += " CodeMirror-linebackground"; }
3656 if (lineView.background) {
3657 if (cls) { lineView.background.className = cls; }
3658 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3659 } else if (cls) {
3660 var wrap = ensureLineWrapped(lineView);
3661 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3662 cm.display.input.setUneditable(lineView.background);
3663 }
3664 }
3665
3666 // Wrapper around buildLineContent which will reuse the structure
3667 // in display.externalMeasured when possible.
3668 function getLineContent(cm, lineView) {
3669 var ext = cm.display.externalMeasured;
3670 if (ext && ext.line == lineView.line) {
3671 cm.display.externalMeasured = null;
3672 lineView.measure = ext.measure;
3673 return ext.built
3674 }
3675 return buildLineContent(cm, lineView)
3676 }
3677
3678 // Redraw the line's text. Interacts with the background and text
3679 // classes because the mode may output tokens that influence these
3680 // classes.
3681 function updateLineText(cm, lineView) {
3682 var cls = lineView.text.className;
3683 var built = getLineContent(cm, lineView);
3684 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3685 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3686 lineView.text = built.pre;
3687 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3688 lineView.bgClass = built.bgClass;
3689 lineView.textClass = built.textClass;
3690 updateLineClasses(cm, lineView);
3691 } else if (cls) {
3692 lineView.text.className = cls;
3693 }
3694 }
3695
3696 function updateLineClasses(cm, lineView) {
3697 updateLineBackground(cm, lineView);
3698 if (lineView.line.wrapClass)
3699 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3700 else if (lineView.node != lineView.text)
3701 { lineView.node.className = ""; }
3702 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3703 lineView.text.className = textClass || "";
3704 }
3705
3706 function updateLineGutter(cm, lineView, lineN, dims) {
3707 if (lineView.gutter) {
3708 lineView.node.removeChild(lineView.gutter);
3709 lineView.gutter = null;
3710 }
3711 if (lineView.gutterBackground) {
3712 lineView.node.removeChild(lineView.gutterBackground);
3713 lineView.gutterBackground = null;
3714 }
3715 if (lineView.line.gutterClass) {
3716 var wrap = ensureLineWrapped(lineView);
3717 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3718 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3719 cm.display.input.setUneditable(lineView.gutterBackground);
3720 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3721 }
3722 var markers = lineView.line.gutterMarkers;
3723 if (cm.options.lineNumbers || markers) {
3724 var wrap$1 = ensureLineWrapped(lineView);
3725 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3726 cm.display.input.setUneditable(gutterWrap);
3727 wrap$1.insertBefore(gutterWrap, lineView.text);
3728 if (lineView.line.gutterClass)
3729 { gutterWrap.className += " " + lineView.line.gutterClass; }
3730 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3731 { lineView.lineNumber = gutterWrap.appendChild(
3732 elt("div", lineNumberFor(cm.options, lineN),
3733 "CodeMirror-linenumber CodeMirror-gutter-elt",
3734 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3735 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
3736 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3737 if (found)
3738 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3739 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3740 } }
3741 }
3742 }
3743
3744 function updateLineWidgets(cm, lineView, dims) {
3745 if (lineView.alignable) { lineView.alignable = null; }
3746 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3747 next = node.nextSibling;
3748 if (node.className == "CodeMirror-linewidget")
3749 { lineView.node.removeChild(node); }
3750 }
3751 insertLineWidgets(cm, lineView, dims);
3752 }
3753
3754 // Build a line's DOM representation from scratch
3755 function buildLineElement(cm, lineView, lineN, dims) {
3756 var built = getLineContent(cm, lineView);
3757 lineView.text = lineView.node = built.pre;
3758 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3759 if (built.textClass) { lineView.textClass = built.textClass; }
3760
3761 updateLineClasses(cm, lineView);
3762 updateLineGutter(cm, lineView, lineN, dims);
3763 insertLineWidgets(cm, lineView, dims);
3764 return lineView.node
3765 }
3766
3767 // A lineView may contain multiple logical lines (when merged by
3768 // collapsed spans). The widgets for all of them need to be drawn.
3769 function insertLineWidgets(cm, lineView, dims) {
3770 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3771 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3772 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3773 }
3774
3775 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3776 if (!line.widgets) { return }
3777 var wrap = ensureLineWrapped(lineView);
3778 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3779 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3780 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3781 positionLineWidget(widget, node, lineView, dims);
3782 cm.display.input.setUneditable(node);
3783 if (allowAbove && widget.above)
3784 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3785 else
3786 { wrap.appendChild(node); }
3787 signalLater(widget, "redraw");
3788 }
3789 }
3790
3791 function positionLineWidget(widget, node, lineView, dims) {
3792 if (widget.noHScroll) {
3793 (lineView.alignable || (lineView.alignable = [])).push(node);
3794 var width = dims.wrapperWidth;
3795 node.style.left = dims.fixedPos + "px";
3796 if (!widget.coverGutter) {
3797 width -= dims.gutterTotalWidth;
3798 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3799 }
3800 node.style.width = width + "px";
3801 }
3802 if (widget.coverGutter) {
3803 node.style.zIndex = 5;
3804 node.style.position = "relative";
3805 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3806 }
3807 }
3808
3809 function widgetHeight(widget) {
3810 if (widget.height != null) { return widget.height }
3811 var cm = widget.doc.cm;
3812 if (!cm) { return 0 }
3813 if (!contains(document.body, widget.node)) {
3814 var parentStyle = "position: relative;";
3815 if (widget.coverGutter)
3816 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3817 if (widget.noHScroll)
3818 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3819 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3820 }
3821 return widget.height = widget.node.parentNode.offsetHeight
3822 }
3823
3824 // Return true when the given mouse event happened in a widget
3825 function eventInWidget(display, e) {
3826 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3827 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3828 (n.parentNode == display.sizer && n != display.mover))
3829 { return true }
3830 }
3831 }
3832
3833 // POSITION MEASUREMENT
3834
3835 function paddingTop(display) {return display.lineSpace.offsetTop}
3836 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3837 function paddingH(display) {
3838 if (display.cachedPaddingH) { return display.cachedPaddingH }
3839 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3840 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3841 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3842 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3843 return data
3844 }
3845
3846 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3847 function displayWidth(cm) {
3848 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3849 }
3850 function displayHeight(cm) {
3851 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3852 }
3853
3854 // Ensure the lineView.wrapping.heights array is populated. This is
3855 // an array of bottom offsets for the lines that make up a drawn
3856 // line. When lineWrapping is on, there might be more than one
3857 // height.
3858 function ensureLineHeights(cm, lineView, rect) {
3859 var wrapping = cm.options.lineWrapping;
3860 var curWidth = wrapping && displayWidth(cm);
3861 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3862 var heights = lineView.measure.heights = [];
3863 if (wrapping) {
3864 lineView.measure.width = curWidth;
3865 var rects = lineView.text.firstChild.getClientRects();
3866 for (var i = 0; i < rects.length - 1; i++) {
3867 var cur = rects[i], next = rects[i + 1];
3868 if (Math.abs(cur.bottom - next.bottom) > 2)
3869 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
3870 }
3871 }
3872 heights.push(rect.bottom - rect.top);
3873 }
3874 }
3875
3876 // Find a line map (mapping character offsets to text nodes) and a
3877 // measurement cache for the given line number. (A line view might
3878 // contain multiple lines when collapsed ranges are present.)
3879 function mapFromLineView(lineView, line, lineN) {
3880 if (lineView.line == line)
3881 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
3882 for (var i = 0; i < lineView.rest.length; i++)
3883 { if (lineView.rest[i] == line)
3884 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
3885 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
3886 { if (lineNo(lineView.rest[i$1]) > lineN)
3887 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
3888 }
3889
3890 // Render a line into the hidden node display.externalMeasured. Used
3891 // when measurement is needed for a line that's not in the viewport.
3892 function updateExternalMeasurement(cm, line) {
3893 line = visualLine(line);
3894 var lineN = lineNo(line);
3895 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
3896 view.lineN = lineN;
3897 var built = view.built = buildLineContent(cm, view);
3898 view.text = built.pre;
3899 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
3900 return view
3901 }
3902
3903 // Get a {top, bottom, left, right} box (in line-local coordinates)
3904 // for a given character.
3905 function measureChar(cm, line, ch, bias) {
3906 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
3907 }
3908
3909 // Find a line view that corresponds to the given line number.
3910 function findViewForLine(cm, lineN) {
3911 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
3912 { return cm.display.view[findViewIndex(cm, lineN)] }
3913 var ext = cm.display.externalMeasured;
3914 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
3915 { return ext }
3916 }
3917
3918 // Measurement can be split in two steps, the set-up work that
3919 // applies to the whole line, and the measurement of the actual
3920 // character. Functions like coordsChar, that need to do a lot of
3921 // measurements in a row, can thus ensure that the set-up work is
3922 // only done once.
3923 function prepareMeasureForLine(cm, line) {
3924 var lineN = lineNo(line);
3925 var view = findViewForLine(cm, lineN);
3926 if (view && !view.text) {
3927 view = null;
3928 } else if (view && view.changes) {
3929 updateLineForChanges(cm, view, lineN, getDimensions(cm));
3930 cm.curOp.forceUpdate = true;
3931 }
3932 if (!view)
3933 { view = updateExternalMeasurement(cm, line); }
3934
3935 var info = mapFromLineView(view, line, lineN);
3936 return {
3937 line: line, view: view, rect: null,
3938 map: info.map, cache: info.cache, before: info.before,
3939 hasHeights: false
3940 }
3941 }
3942
3943 // Given a prepared measurement object, measures the position of an
3944 // actual character (or fetches it from the cache).
3945 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
3946 if (prepared.before) { ch = -1; }
3947 var key = ch + (bias || ""), found;
3948 if (prepared.cache.hasOwnProperty(key)) {
3949 found = prepared.cache[key];
3950 } else {
3951 if (!prepared.rect)
3952 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
3953 if (!prepared.hasHeights) {
3954 ensureLineHeights(cm, prepared.view, prepared.rect);
3955 prepared.hasHeights = true;
3956 }
3957 found = measureCharInner(cm, prepared, ch, bias);
3958 if (!found.bogus) { prepared.cache[key] = found; }
3959 }
3960 return {left: found.left, right: found.right,
3961 top: varHeight ? found.rtop : found.top,
3962 bottom: varHeight ? found.rbottom : found.bottom}
3963 }
3964
3965 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
3966
3967 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
3968 var node, start, end, collapse, mStart, mEnd;
3969 // First, search the line map for the text node corresponding to,
3970 // or closest to, the target character.
3971 for (var i = 0; i < map$$1.length; i += 3) {
3972 mStart = map$$1[i];
3973 mEnd = map$$1[i + 1];
3974 if (ch < mStart) {
3975 start = 0; end = 1;
3976 collapse = "left";
3977 } else if (ch < mEnd) {
3978 start = ch - mStart;
3979 end = start + 1;
3980 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
3981 end = mEnd - mStart;
3982 start = end - 1;
3983 if (ch >= mEnd) { collapse = "right"; }
3984 }
3985 if (start != null) {
3986 node = map$$1[i + 2];
3987 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
3988 { collapse = bias; }
3989 if (bias == "left" && start == 0)
3990 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
3991 node = map$$1[(i -= 3) + 2];
3992 collapse = "left";
3993 } }
3994 if (bias == "right" && start == mEnd - mStart)
3995 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
3996 node = map$$1[(i += 3) + 2];
3997 collapse = "right";
3998 } }
3999 break
4000 }
4001 }
4002 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4003 }
4004
4005 function getUsefulRect(rects, bias) {
4006 var rect = nullRect;
4007 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4008 if ((rect = rects[i]).left != rect.right) { break }
4009 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4010 if ((rect = rects[i$1]).left != rect.right) { break }
4011 } }
4012 return rect
4013 }
4014
4015 function measureCharInner(cm, prepared, ch, bias) {
4016 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4017 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4018
4019 var rect;
4020 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4021 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4022 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4023 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4024 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4025 { rect = node.parentNode.getBoundingClientRect(); }
4026 else
4027 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4028 if (rect.left || rect.right || start == 0) { break }
4029 end = start;
4030 start = start - 1;
4031 collapse = "right";
4032 }
4033 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4034 } else { // If it is a widget, simply get the box for the whole widget.
4035 if (start > 0) { collapse = bias = "right"; }
4036 var rects;
4037 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4038 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4039 else
4040 { rect = node.getBoundingClientRect(); }
4041 }
4042 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4043 var rSpan = node.parentNode.getClientRects()[0];
4044 if (rSpan)
4045 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4046 else
4047 { rect = nullRect; }
4048 }
4049
4050 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4051 var mid = (rtop + rbot) / 2;
4052 var heights = prepared.view.measure.heights;
4053 var i = 0;
4054 for (; i < heights.length - 1; i++)
4055 { if (mid < heights[i]) { break } }
4056 var top = i ? heights[i - 1] : 0, bot = heights[i];
4057 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4058 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4059 top: top, bottom: bot};
4060 if (!rect.left && !rect.right) { result.bogus = true; }
4061 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4062
4063 return result
4064 }
4065
4066 // Work around problem with bounding client rects on ranges being
4067 // returned incorrectly when zoomed on IE10 and below.
4068 function maybeUpdateRectForZooming(measure, rect) {
4069 if (!window.screen || screen.logicalXDPI == null ||
4070 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4071 { return rect }
4072 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4073 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4074 return {left: rect.left * scaleX, right: rect.right * scaleX,
4075 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4076 }
4077
4078 function clearLineMeasurementCacheFor(lineView) {
4079 if (lineView.measure) {
4080 lineView.measure.cache = {};
4081 lineView.measure.heights = null;
4082 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4083 { lineView.measure.caches[i] = {}; } }
4084 }
4085 }
4086
4087 function clearLineMeasurementCache(cm) {
4088 cm.display.externalMeasure = null;
4089 removeChildren(cm.display.lineMeasure);
4090 for (var i = 0; i < cm.display.view.length; i++)
4091 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4092 }
4093
4094 function clearCaches(cm) {
4095 clearLineMeasurementCache(cm);
4096 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4097 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4098 cm.display.lineNumChars = null;
4099 }
4100
4101 function pageScrollX() {
4102 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4103 // which causes page_Offset and bounding client rects to use
4104 // different reference viewports and invalidate our calculations.
4105 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4106 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4107 }
4108 function pageScrollY() {
4109 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4110 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4111 }
4112
4113 function widgetTopHeight(lineObj) {
4114 var height = 0;
4115 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4116 { height += widgetHeight(lineObj.widgets[i]); } } }
4117 return height
4118 }
4119
4120 // Converts a {top, bottom, left, right} box from line-local
4121 // coordinates into another coordinate system. Context may be one of
4122 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4123 // or "page".
4124 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4125 if (!includeWidgets) {
4126 var height = widgetTopHeight(lineObj);
4127 rect.top += height; rect.bottom += height;
4128 }
4129 if (context == "line") { return rect }
4130 if (!context) { context = "local"; }
4131 var yOff = heightAtLine(lineObj);
4132 if (context == "local") { yOff += paddingTop(cm.display); }
4133 else { yOff -= cm.display.viewOffset; }
4134 if (context == "page" || context == "window") {
4135 var lOff = cm.display.lineSpace.getBoundingClientRect();
4136 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4137 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4138 rect.left += xOff; rect.right += xOff;
4139 }
4140 rect.top += yOff; rect.bottom += yOff;
4141 return rect
4142 }
4143
4144 // Coverts a box from "div" coords to another coordinate system.
4145 // Context may be "window", "page", "div", or "local"./null.
4146 function fromCoordSystem(cm, coords, context) {
4147 if (context == "div") { return coords }
4148 var left = coords.left, top = coords.top;
4149 // First move into "page" coordinate system
4150 if (context == "page") {
4151 left -= pageScrollX();
4152 top -= pageScrollY();
4153 } else if (context == "local" || !context) {
4154 var localBox = cm.display.sizer.getBoundingClientRect();
4155 left += localBox.left;
4156 top += localBox.top;
4157 }
4158
4159 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4160 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4161 }
4162
4163 function charCoords(cm, pos, context, lineObj, bias) {
4164 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4165 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4166 }
4167
4168 // Returns a box for a given cursor position, which may have an
4169 // 'other' property containing the position of the secondary cursor
4170 // on a bidi boundary.
4171 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4172 // and after `char - 1` in writing order of `char - 1`
4173 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4174 // and before `char` in writing order of `char`
4175 // Examples (upper-case letters are RTL, lower-case are LTR):
4176 // Pos(0, 1, ...)
4177 // before after
4178 // ab a|b a|b
4179 // aB a|B aB|
4180 // Ab |Ab A|b
4181 // AB B|A B|A
4182 // Every position after the last character on a line is considered to stick
4183 // to the last character on the line.
4184 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4185 lineObj = lineObj || getLine(cm.doc, pos.line);
4186 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4187 function get(ch, right) {
4188 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4189 if (right) { m.left = m.right; } else { m.right = m.left; }
4190 return intoCoordSystem(cm, lineObj, m, context)
4191 }
4192 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4193 if (ch >= lineObj.text.length) {
4194 ch = lineObj.text.length;
4195 sticky = "before";
4196 } else if (ch <= 0) {
4197 ch = 0;
4198 sticky = "after";
4199 }
4200 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4201
4202 function getBidi(ch, partPos, invert) {
4203 var part = order[partPos], right = part.level == 1;
4204 return get(invert ? ch - 1 : ch, right != invert)
4205 }
4206 var partPos = getBidiPartAt(order, ch, sticky);
4207 var other = bidiOther;
4208 var val = getBidi(ch, partPos, sticky == "before");
4209 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4210 return val
4211 }
4212
4213 // Used to cheaply estimate the coordinates for a position. Used for
4214 // intermediate scroll updates.
4215 function estimateCoords(cm, pos) {
4216 var left = 0;
4217 pos = clipPos(cm.doc, pos);
4218 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4219 var lineObj = getLine(cm.doc, pos.line);
4220 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4221 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4222 }
4223
4224 // Positions returned by coordsChar contain some extra information.
4225 // xRel is the relative x position of the input coordinates compared
4226 // to the found position (so xRel > 0 means the coordinates are to
4227 // the right of the character position, for example). When outside
4228 // is true, that means the coordinates lie outside the line's
4229 // vertical range.
4230 function PosWithInfo(line, ch, sticky, outside, xRel) {
4231 var pos = Pos(line, ch, sticky);
4232 pos.xRel = xRel;
4233 if (outside) { pos.outside = true; }
4234 return pos
4235 }
4236
4237 // Compute the character position closest to the given coordinates.
4238 // Input must be lineSpace-local ("div" coordinate system).
4239 function coordsChar(cm, x, y) {
4240 var doc = cm.doc;
4241 y += cm.display.viewOffset;
4242 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4243 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4244 if (lineN > last)
4245 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4246 if (x < 0) { x = 0; }
4247
4248 var lineObj = getLine(doc, lineN);
4249 for (;;) {
4250 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4251 var merged = collapsedSpanAtEnd(lineObj);
4252 var mergedPos = merged && merged.find(0, true);
4253 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
4254 { lineN = lineNo(lineObj = mergedPos.to.line); }
4255 else
4256 { return found }
4257 }
4258 }
4259
4260 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4261 y -= widgetTopHeight(lineObj);
4262 var end = lineObj.text.length;
4263 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4264 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4265 return {begin: begin, end: end}
4266 }
4267
4268 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4269 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4270 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4271 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4272 }
4273
4274 // Returns true if the given side of a box is after the given
4275 // coordinates, in top-to-bottom, left-to-right order.
4276 function boxIsAfter(box, x, y, left) {
4277 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4278 }
4279
4280 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4281 // Move y into line-local coordinate space
4282 y -= heightAtLine(lineObj);
4283 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4284 // When directly calling `measureCharPrepared`, we have to adjust
4285 // for the widgets at this line.
4286 var widgetHeight$$1 = widgetTopHeight(lineObj);
4287 var begin = 0, end = lineObj.text.length, ltr = true;
4288
4289 var order = getOrder(lineObj, cm.doc.direction);
4290 // If the line isn't plain left-to-right text, first figure out
4291 // which bidi section the coordinates fall into.
4292 if (order) {
4293 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4294 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
4295 ltr = part.level != 1;
4296 // The awkward -1 offsets are needed because findFirst (called
4297 // on these below) will treat its first bound as inclusive,
4298 // second as exclusive, but we want to actually address the
4299 // characters in the part's range
4300 begin = ltr ? part.from : part.to - 1;
4301 end = ltr ? part.to : part.from - 1;
4302 }
4303
4304 // A binary search to find the first character whose bounding box
4305 // starts after the coordinates. If we run across any whose box wrap
4306 // the coordinates, store that.
4307 var chAround = null, boxAround = null;
4308 var ch = findFirst(function (ch) {
4309 var box = measureCharPrepared(cm, preparedMeasure, ch);
4310 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
4311 if (!boxIsAfter(box, x, y, false)) { return false }
4312 if (box.top <= y && box.left <= x) {
4313 chAround = ch;
4314 boxAround = box;
4315 }
4316 return true
4317 }, begin, end);
4318
4319 var baseX, sticky, outside = false;
4320 // If a box around the coordinates was found, use that
4321 if (boxAround) {
4322 // Distinguish coordinates nearer to the left or right side of the box
4323 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4324 ch = chAround + (atStart ? 0 : 1);
4325 sticky = atStart ? "after" : "before";
4326 baseX = atLeft ? boxAround.left : boxAround.right;
4327 } else {
4328 // (Adjust for extended bound, if necessary.)
4329 if (!ltr && (ch == end || ch == begin)) { ch++; }
4330 // To determine which side to associate with, get the box to the
4331 // left of the character and compare it's vertical position to the
4332 // coordinates
4333 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4334 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
4335 "after" : "before";
4336 // Now get accurate coordinates for this place, in order to get a
4337 // base X position
4338 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
4339 baseX = coords.left;
4340 outside = y < coords.top || y >= coords.bottom;
4341 }
4342
4343 ch = skipExtendingChars(lineObj.text, ch, 1);
4344 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
4345 }
4346
4347 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
4348 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4349 // situation, we can take this ordering to correspond to the visual
4350 // ordering. This finds the first part whose end is after the given
4351 // coordinates.
4352 var index = findFirst(function (i) {
4353 var part = order[i], ltr = part.level != 1;
4354 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
4355 "line", lineObj, preparedMeasure), x, y, true)
4356 }, 0, order.length - 1);
4357 var part = order[index];
4358 // If this isn't the first part, the part's start is also after
4359 // the coordinates, and the coordinates aren't on the same line as
4360 // that start, move one part back.
4361 if (index > 0) {
4362 var ltr = part.level != 1;
4363 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
4364 "line", lineObj, preparedMeasure);
4365 if (boxIsAfter(start, x, y, true) && start.top > y)
4366 { part = order[index - 1]; }
4367 }
4368 return part
4369 }
4370
4371 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4372 // In a wrapped line, rtl text on wrapping boundaries can do things
4373 // that don't correspond to the ordering in our `order` array at
4374 // all, so a binary search doesn't work, and we want to return a
4375 // part that only spans one line so that the binary search in
4376 // coordsCharInner is safe. As such, we first find the extent of the
4377 // wrapped line, and then do a flat search in which we discard any
4378 // spans that aren't on the line.
4379 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4380 var begin = ref.begin;
4381 var end = ref.end;
4382 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4383 var part = null, closestDist = null;
4384 for (var i = 0; i < order.length; i++) {
4385 var p = order[i];
4386 if (p.from >= end || p.to <= begin) { continue }
4387 var ltr = p.level != 1;
4388 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4389 // Weigh against spans ending before this, so that they are only
4390 // picked if nothing ends after
4391 var dist = endX < x ? x - endX + 1e9 : endX - x;
4392 if (!part || closestDist > dist) {
4393 part = p;
4394 closestDist = dist;
4395 }
4396 }
4397 if (!part) { part = order[order.length - 1]; }
4398 // Clip the part to the wrapped line.
4399 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4400 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4401 return part
4402 }
4403
4404 var measureText;
4405 // Compute the default text height.
4406 function textHeight(display) {
4407 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4408 if (measureText == null) {
4409 measureText = elt("pre");
4410 // Measure a bunch of lines, for browsers that compute
4411 // fractional heights.
4412 for (var i = 0; i < 49; ++i) {
4413 measureText.appendChild(document.createTextNode("x"));
4414 measureText.appendChild(elt("br"));
4415 }
4416 measureText.appendChild(document.createTextNode("x"));
4417 }
4418 removeChildrenAndAdd(display.measure, measureText);
4419 var height = measureText.offsetHeight / 50;
4420 if (height > 3) { display.cachedTextHeight = height; }
4421 removeChildren(display.measure);
4422 return height || 1
4423 }
4424
4425 // Compute the default character width.
4426 function charWidth(display) {
4427 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4428 var anchor = elt("span", "xxxxxxxxxx");
4429 var pre = elt("pre", [anchor]);
4430 removeChildrenAndAdd(display.measure, pre);
4431 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4432 if (width > 2) { display.cachedCharWidth = width; }
4433 return width || 10
4434 }
4435
4436 // Do a bulk-read of the DOM positions and sizes needed to draw the
4437 // view, so that we don't interleave reading and writing to the DOM.
4438 function getDimensions(cm) {
4439 var d = cm.display, left = {}, width = {};
4440 var gutterLeft = d.gutters.clientLeft;
4441 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4442 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
4443 width[cm.options.gutters[i]] = n.clientWidth;
4444 }
4445 return {fixedPos: compensateForHScroll(d),
4446 gutterTotalWidth: d.gutters.offsetWidth,
4447 gutterLeft: left,
4448 gutterWidth: width,
4449 wrapperWidth: d.wrapper.clientWidth}
4450 }
4451
4452 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4453 // but using getBoundingClientRect to get a sub-pixel-accurate
4454 // result.
4455 function compensateForHScroll(display) {
4456 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4457 }
4458
4459 // Returns a function that estimates the height of a line, to use as
4460 // first approximation until the line becomes visible (and is thus
4461 // properly measurable).
4462 function estimateHeight(cm) {
4463 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4464 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4465 return function (line) {
4466 if (lineIsHidden(cm.doc, line)) { return 0 }
4467
4468 var widgetsHeight = 0;
4469 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4470 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4471 } }
4472
4473 if (wrapping)
4474 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4475 else
4476 { return widgetsHeight + th }
4477 }
4478 }
4479
4480 function estimateLineHeights(cm) {
4481 var doc = cm.doc, est = estimateHeight(cm);
4482 doc.iter(function (line) {
4483 var estHeight = est(line);
4484 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4485 });
4486 }
4487
4488 // Given a mouse event, find the corresponding position. If liberal
4489 // is false, it checks whether a gutter or scrollbar was clicked,
4490 // and returns null if it was. forRect is used by rectangular
4491 // selections, and tries to estimate a character position even for
4492 // coordinates beyond the right of the text.
4493 function posFromMouse(cm, e, liberal, forRect) {
4494 var display = cm.display;
4495 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4496
4497 var x, y, space = display.lineSpace.getBoundingClientRect();
4498 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4499 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4500 catch (e) { return null }
4501 var coords = coordsChar(cm, x, y), line;
4502 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4503 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4504 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4505 }
4506 return coords
4507 }
4508
4509 // Find the view element corresponding to a given line. Return null
4510 // when the line isn't visible.
4511 function findViewIndex(cm, n) {
4512 if (n >= cm.display.viewTo) { return null }
4513 n -= cm.display.viewFrom;
4514 if (n < 0) { return null }
4515 var view = cm.display.view;
4516 for (var i = 0; i < view.length; i++) {
4517 n -= view[i].size;
4518 if (n < 0) { return i }
4519 }
4520 }
4521
4522 function updateSelection(cm) {
4523 cm.display.input.showSelection(cm.display.input.prepareSelection());
4524 }
4525
4526 function prepareSelection(cm, primary) {
4527 if ( primary === void 0 ) primary = true;
4528
4529 var doc = cm.doc, result = {};
4530 var curFragment = result.cursors = document.createDocumentFragment();
4531 var selFragment = result.selection = document.createDocumentFragment();
4532
4533 for (var i = 0; i < doc.sel.ranges.length; i++) {
4534 if (!primary && i == doc.sel.primIndex) { continue }
4535 var range$$1 = doc.sel.ranges[i];
4536 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4537 var collapsed = range$$1.empty();
4538 if (collapsed || cm.options.showCursorWhenSelecting)
4539 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4540 if (!collapsed)
4541 { drawSelectionRange(cm, range$$1, selFragment); }
4542 }
4543 return result
4544 }
4545
4546 // Draws a cursor for the given range
4547 function drawSelectionCursor(cm, head, output) {
4548 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4549
4550 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4551 cursor.style.left = pos.left + "px";
4552 cursor.style.top = pos.top + "px";
4553 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4554
4555 if (pos.other) {
4556 // Secondary cursor, shown when on a 'jump' in bi-directional text
4557 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4558 otherCursor.style.display = "";
4559 otherCursor.style.left = pos.other.left + "px";
4560 otherCursor.style.top = pos.other.top + "px";
4561 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4562 }
4563 }
4564
4565 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4566
4567 // Draws the given range as a highlighted selection
4568 function drawSelectionRange(cm, range$$1, output) {
4569 var display = cm.display, doc = cm.doc;
4570 var fragment = document.createDocumentFragment();
4571 var padding = paddingH(cm.display), leftSide = padding.left;
4572 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4573 var docLTR = doc.direction == "ltr";
4574
4575 function add(left, top, width, bottom) {
4576 if (top < 0) { top = 0; }
4577 top = Math.round(top);
4578 bottom = Math.round(bottom);
4579 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")));
4580 }
4581
4582 function drawForLine(line, fromArg, toArg) {
4583 var lineObj = getLine(doc, line);
4584 var lineLen = lineObj.text.length;
4585 var start, end;
4586 function coords(ch, bias) {
4587 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4588 }
4589
4590 function wrapX(pos, dir, side) {
4591 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4592 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4593 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4594 return coords(ch, prop)[prop]
4595 }
4596
4597 var order = getOrder(lineObj, doc.direction);
4598 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4599 var ltr = dir == "ltr";
4600 var fromPos = coords(from, ltr ? "left" : "right");
4601 var toPos = coords(to - 1, ltr ? "right" : "left");
4602
4603 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4604 var first = i == 0, last = !order || i == order.length - 1;
4605 if (toPos.top - fromPos.top <= 3) { // Single line
4606 var openLeft = (docLTR ? openStart : openEnd) && first;
4607 var openRight = (docLTR ? openEnd : openStart) && last;
4608 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4609 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4610 add(left, fromPos.top, right - left, fromPos.bottom);
4611 } else { // Multiple lines
4612 var topLeft, topRight, botLeft, botRight;
4613 if (ltr) {
4614 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4615 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4616 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4617 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4618 } else {
4619 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4620 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4621 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4622 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4623 }
4624 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4625 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4626 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4627 }
4628
4629 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4630 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4631 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4632 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4633 });
4634 return {start: start, end: end}
4635 }
4636
4637 var sFrom = range$$1.from(), sTo = range$$1.to();
4638 if (sFrom.line == sTo.line) {
4639 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4640 } else {
4641 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4642 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4643 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4644 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4645 if (singleVLine) {
4646 if (leftEnd.top < rightStart.top - 2) {
4647 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4648 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4649 } else {
4650 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4651 }
4652 }
4653 if (leftEnd.bottom < rightStart.top)
4654 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4655 }
4656
4657 output.appendChild(fragment);
4658 }
4659
4660 // Cursor-blinking
4661 function restartBlink(cm) {
4662 if (!cm.state.focused) { return }
4663 var display = cm.display;
4664 clearInterval(display.blinker);
4665 var on = true;
4666 display.cursorDiv.style.visibility = "";
4667 if (cm.options.cursorBlinkRate > 0)
4668 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4669 cm.options.cursorBlinkRate); }
4670 else if (cm.options.cursorBlinkRate < 0)
4671 { display.cursorDiv.style.visibility = "hidden"; }
4672 }
4673
4674 function ensureFocus(cm) {
4675 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4676 }
4677
4678 function delayBlurEvent(cm) {
4679 cm.state.delayingBlurEvent = true;
4680 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4681 cm.state.delayingBlurEvent = false;
4682 onBlur(cm);
4683 } }, 100);
4684 }
4685
4686 function onFocus(cm, e) {
4687 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4688
4689 if (cm.options.readOnly == "nocursor") { return }
4690 if (!cm.state.focused) {
4691 signal(cm, "focus", cm, e);
4692 cm.state.focused = true;
4693 addClass(cm.display.wrapper, "CodeMirror-focused");
4694 // This test prevents this from firing when a context
4695 // menu is closed (since the input reset would kill the
4696 // select-all detection hack)
4697 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4698 cm.display.input.reset();
4699 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4700 }
4701 cm.display.input.receivedFocus();
4702 }
4703 restartBlink(cm);
4704 }
4705 function onBlur(cm, e) {
4706 if (cm.state.delayingBlurEvent) { return }
4707
4708 if (cm.state.focused) {
4709 signal(cm, "blur", cm, e);
4710 cm.state.focused = false;
4711 rmClass(cm.display.wrapper, "CodeMirror-focused");
4712 }
4713 clearInterval(cm.display.blinker);
4714 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4715 }
4716
4717 // Read the actual heights of the rendered lines, and update their
4718 // stored heights to match.
4719 function updateHeightsInViewport(cm) {
4720 var display = cm.display;
4721 var prevBottom = display.lineDiv.offsetTop;
4722 for (var i = 0; i < display.view.length; i++) {
4723 var cur = display.view[i], height = (void 0);
4724 if (cur.hidden) { continue }
4725 if (ie && ie_version < 8) {
4726 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4727 height = bot - prevBottom;
4728 prevBottom = bot;
4729 } else {
4730 var box = cur.node.getBoundingClientRect();
4731 height = box.bottom - box.top;
4732 }
4733 var diff = cur.line.height - height;
4734 if (height < 2) { height = textHeight(display); }
4735 if (diff > .005 || diff < -.005) {
4736 updateLineHeight(cur.line, height);
4737 updateWidgetHeight(cur.line);
4738 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4739 { updateWidgetHeight(cur.rest[j]); } }
4740 }
4741 }
4742 }
4743
4744 // Read and store the height of line widgets associated with the
4745 // given line.
4746 function updateWidgetHeight(line) {
4747 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4748 var w = line.widgets[i], parent = w.node.parentNode;
4749 if (parent) { w.height = parent.offsetHeight; }
4750 } }
4751 }
4752
4753 // Compute the lines that are visible in a given viewport (defaults
4754 // the the current scroll position). viewport may contain top,
4755 // height, and ensure (see op.scrollToPos) properties.
4756 function visibleLines(display, doc, viewport) {
4757 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4758 top = Math.floor(top - paddingTop(display));
4759 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4760
4761 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4762 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4763 // forces those lines into the viewport (if possible).
4764 if (viewport && viewport.ensure) {
4765 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4766 if (ensureFrom < from) {
4767 from = ensureFrom;
4768 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4769 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4770 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4771 to = ensureTo;
4772 }
4773 }
4774 return {from: from, to: Math.max(to, from + 1)}
4775 }
4776
4777 // Re-align line numbers and gutter marks to compensate for
4778 // horizontal scrolling.
4779 function alignHorizontally(cm) {
4780 var display = cm.display, view = display.view;
4781 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4782 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4783 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4784 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4785 if (cm.options.fixedGutter) {
4786 if (view[i].gutter)
4787 { view[i].gutter.style.left = left; }
4788 if (view[i].gutterBackground)
4789 { view[i].gutterBackground.style.left = left; }
4790 }
4791 var align = view[i].alignable;
4792 if (align) { for (var j = 0; j < align.length; j++)
4793 { align[j].style.left = left; } }
4794 } }
4795 if (cm.options.fixedGutter)
4796 { display.gutters.style.left = (comp + gutterW) + "px"; }
4797 }
4798
4799 // Used to ensure that the line number gutter is still the right
4800 // size for the current document size. Returns true when an update
4801 // is needed.
4802 function maybeUpdateLineNumberWidth(cm) {
4803 if (!cm.options.lineNumbers) { return false }
4804 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4805 if (last.length != display.lineNumChars) {
4806 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4807 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4808 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4809 display.lineGutter.style.width = "";
4810 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4811 display.lineNumWidth = display.lineNumInnerWidth + padding;
4812 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4813 display.lineGutter.style.width = display.lineNumWidth + "px";
4814 updateGutterSpace(cm);
4815 return true
4816 }
4817 return false
4818 }
4819
4820 // SCROLLING THINGS INTO VIEW
4821
4822 // If an editor sits on the top or bottom of the window, partially
4823 // scrolled out of view, this ensures that the cursor is visible.
4824 function maybeScrollWindow(cm, rect) {
4825 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4826
4827 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4828 if (rect.top + box.top < 0) { doScroll = true; }
4829 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4830 if (doScroll != null && !phantom) {
4831 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;"));
4832 cm.display.lineSpace.appendChild(scrollNode);
4833 scrollNode.scrollIntoView(doScroll);
4834 cm.display.lineSpace.removeChild(scrollNode);
4835 }
4836 }
4837
4838 // Scroll a given position into view (immediately), verifying that
4839 // it actually became visible (as line heights are accurately
4840 // measured, the position of something may 'drift' during drawing).
4841 function scrollPosIntoView(cm, pos, end, margin) {
4842 if (margin == null) { margin = 0; }
4843 var rect;
4844 if (!cm.options.lineWrapping && pos == end) {
4845 // Set pos and end to the cursor positions around the character pos sticks to
4846 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4847 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4848 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
4849 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
4850 }
4851 for (var limit = 0; limit < 5; limit++) {
4852 var changed = false;
4853 var coords = cursorCoords(cm, pos);
4854 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4855 rect = {left: Math.min(coords.left, endCoords.left),
4856 top: Math.min(coords.top, endCoords.top) - margin,
4857 right: Math.max(coords.left, endCoords.left),
4858 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
4859 var scrollPos = calculateScrollPos(cm, rect);
4860 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4861 if (scrollPos.scrollTop != null) {
4862 updateScrollTop(cm, scrollPos.scrollTop);
4863 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
4864 }
4865 if (scrollPos.scrollLeft != null) {
4866 setScrollLeft(cm, scrollPos.scrollLeft);
4867 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
4868 }
4869 if (!changed) { break }
4870 }
4871 return rect
4872 }
4873
4874 // Scroll a given set of coordinates into view (immediately).
4875 function scrollIntoView(cm, rect) {
4876 var scrollPos = calculateScrollPos(cm, rect);
4877 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
4878 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
4879 }
4880
4881 // Calculate a new scroll position needed to scroll the given
4882 // rectangle into view. Returns an object with scrollTop and
4883 // scrollLeft properties. When these are undefined, the
4884 // vertical/horizontal position does not need to be adjusted.
4885 function calculateScrollPos(cm, rect) {
4886 var display = cm.display, snapMargin = textHeight(cm.display);
4887 if (rect.top < 0) { rect.top = 0; }
4888 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4889 var screen = displayHeight(cm), result = {};
4890 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
4891 var docBottom = cm.doc.height + paddingVert(display);
4892 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
4893 if (rect.top < screentop) {
4894 result.scrollTop = atTop ? 0 : rect.top;
4895 } else if (rect.bottom > screentop + screen) {
4896 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
4897 if (newTop != screentop) { result.scrollTop = newTop; }
4898 }
4899
4900 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4901 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4902 var tooWide = rect.right - rect.left > screenw;
4903 if (tooWide) { rect.right = rect.left + screenw; }
4904 if (rect.left < 10)
4905 { result.scrollLeft = 0; }
4906 else if (rect.left < screenleft)
4907 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
4908 else if (rect.right > screenw + screenleft - 3)
4909 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
4910 return result
4911 }
4912
4913 // Store a relative adjustment to the scroll position in the current
4914 // operation (to be applied when the operation finishes).
4915 function addToScrollTop(cm, top) {
4916 if (top == null) { return }
4917 resolveScrollToPos(cm);
4918 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4919 }
4920
4921 // Make sure that at the end of the operation the current cursor is
4922 // shown.
4923 function ensureCursorVisible(cm) {
4924 resolveScrollToPos(cm);
4925 var cur = cm.getCursor();
4926 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
4927 }
4928
4929 function scrollToCoords(cm, x, y) {
4930 if (x != null || y != null) { resolveScrollToPos(cm); }
4931 if (x != null) { cm.curOp.scrollLeft = x; }
4932 if (y != null) { cm.curOp.scrollTop = y; }
4933 }
4934
4935 function scrollToRange(cm, range$$1) {
4936 resolveScrollToPos(cm);
4937 cm.curOp.scrollToPos = range$$1;
4938 }
4939
4940 // When an operation has its scrollToPos property set, and another
4941 // scroll action is applied before the end of the operation, this
4942 // 'simulates' scrolling that position into view in a cheap way, so
4943 // that the effect of intermediate scroll commands is not ignored.
4944 function resolveScrollToPos(cm) {
4945 var range$$1 = cm.curOp.scrollToPos;
4946 if (range$$1) {
4947 cm.curOp.scrollToPos = null;
4948 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
4949 scrollToCoordsRange(cm, from, to, range$$1.margin);
4950 }
4951 }
4952
4953 function scrollToCoordsRange(cm, from, to, margin) {
4954 var sPos = calculateScrollPos(cm, {
4955 left: Math.min(from.left, to.left),
4956 top: Math.min(from.top, to.top) - margin,
4957 right: Math.max(from.right, to.right),
4958 bottom: Math.max(from.bottom, to.bottom) + margin
4959 });
4960 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
4961 }
4962
4963 // Sync the scrollable area and scrollbars, ensure the viewport
4964 // covers the visible area.
4965 function updateScrollTop(cm, val) {
4966 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
4967 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
4968 setScrollTop(cm, val, true);
4969 if (gecko) { updateDisplaySimple(cm); }
4970 startWorker(cm, 100);
4971 }
4972
4973 function setScrollTop(cm, val, forceScroll) {
4974 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
4975 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
4976 cm.doc.scrollTop = val;
4977 cm.display.scrollbars.setScrollTop(val);
4978 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
4979 }
4980
4981 // Sync scroller and scrollbar, ensure the gutter elements are
4982 // aligned.
4983 function setScrollLeft(cm, val, isScroller, forceScroll) {
4984 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
4985 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
4986 cm.doc.scrollLeft = val;
4987 alignHorizontally(cm);
4988 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
4989 cm.display.scrollbars.setScrollLeft(val);
4990 }
4991
4992 // SCROLLBARS
4993
4994 // Prepare DOM reads needed to update the scrollbars. Done in one
4995 // shot to minimize update/measure roundtrips.
4996 function measureForScrollbars(cm) {
4997 var d = cm.display, gutterW = d.gutters.offsetWidth;
4998 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
4999 return {
5000 clientHeight: d.scroller.clientHeight,
5001 viewHeight: d.wrapper.clientHeight,
5002 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5003 viewWidth: d.wrapper.clientWidth,
5004 barLeft: cm.options.fixedGutter ? gutterW : 0,
5005 docHeight: docH,
5006 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5007 nativeBarWidth: d.nativeBarWidth,
5008 gutterWidth: gutterW
5009 }
5010 }
5011
5012 var NativeScrollbars = function(place, scroll, cm) {
5013 this.cm = cm;
5014 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5015 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5016 place(vert); place(horiz);
5017
5018 on(vert, "scroll", function () {
5019 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5020 });
5021 on(horiz, "scroll", function () {
5022 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5023 });
5024
5025 this.checkedZeroWidth = false;
5026 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5027 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5028 };
5029
5030 NativeScrollbars.prototype.update = function (measure) {
5031 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5032 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5033 var sWidth = measure.nativeBarWidth;
5034
5035 if (needsV) {
5036 this.vert.style.display = "block";
5037 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5038 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5039 // A bug in IE8 can cause this value to be negative, so guard it.
5040 this.vert.firstChild.style.height =
5041 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5042 } else {
5043 this.vert.style.display = "";
5044 this.vert.firstChild.style.height = "0";
5045 }
5046
5047 if (needsH) {
5048 this.horiz.style.display = "block";
5049 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5050 this.horiz.style.left = measure.barLeft + "px";
5051 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5052 this.horiz.firstChild.style.width =
5053 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5054 } else {
5055 this.horiz.style.display = "";
5056 this.horiz.firstChild.style.width = "0";
5057 }
5058
5059 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5060 if (sWidth == 0) { this.zeroWidthHack(); }
5061 this.checkedZeroWidth = true;
5062 }
5063
5064 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5065 };
5066
5067 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5068 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5069 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5070 };
5071
5072 NativeScrollbars.prototype.setScrollTop = function (pos) {
5073 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5074 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5075 };
5076
5077 NativeScrollbars.prototype.zeroWidthHack = function () {
5078 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5079 this.horiz.style.height = this.vert.style.width = w;
5080 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5081 this.disableHoriz = new Delayed;
5082 this.disableVert = new Delayed;
5083 };
5084
5085 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5086 bar.style.pointerEvents = "auto";
5087 function maybeDisable() {
5088 // To find out whether the scrollbar is still visible, we
5089 // check whether the element under the pixel in the bottom
5090 // right corner of the scrollbar box is the scrollbar box
5091 // itself (when the bar is still visible) or its filler child
5092 // (when the bar is hidden). If it is still visible, we keep
5093 // it enabled, if it's hidden, we disable pointer events.
5094 var box = bar.getBoundingClientRect();
5095 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5096 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5097 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5098 else { delay.set(1000, maybeDisable); }
5099 }
5100 delay.set(1000, maybeDisable);
5101 };
5102
5103 NativeScrollbars.prototype.clear = function () {
5104 var parent = this.horiz.parentNode;
5105 parent.removeChild(this.horiz);
5106 parent.removeChild(this.vert);
5107 };
5108
5109 var NullScrollbars = function () {};
5110
5111 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5112 NullScrollbars.prototype.setScrollLeft = function () {};
5113 NullScrollbars.prototype.setScrollTop = function () {};
5114 NullScrollbars.prototype.clear = function () {};
5115
5116 function updateScrollbars(cm, measure) {
5117 if (!measure) { measure = measureForScrollbars(cm); }
5118 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5119 updateScrollbarsInner(cm, measure);
5120 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5121 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5122 { updateHeightsInViewport(cm); }
5123 updateScrollbarsInner(cm, measureForScrollbars(cm));
5124 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5125 }
5126 }
5127
5128 // Re-synchronize the fake scrollbars with the actual size of the
5129 // content.
5130 function updateScrollbarsInner(cm, measure) {
5131 var d = cm.display;
5132 var sizes = d.scrollbars.update(measure);
5133
5134 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5135 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5136 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5137
5138 if (sizes.right && sizes.bottom) {
5139 d.scrollbarFiller.style.display = "block";
5140 d.scrollbarFiller.style.height = sizes.bottom + "px";
5141 d.scrollbarFiller.style.width = sizes.right + "px";
5142 } else { d.scrollbarFiller.style.display = ""; }
5143 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5144 d.gutterFiller.style.display = "block";
5145 d.gutterFiller.style.height = sizes.bottom + "px";
5146 d.gutterFiller.style.width = measure.gutterWidth + "px";
5147 } else { d.gutterFiller.style.display = ""; }
5148 }
5149
5150 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5151
5152 function initScrollbars(cm) {
5153 if (cm.display.scrollbars) {
5154 cm.display.scrollbars.clear();
5155 if (cm.display.scrollbars.addClass)
5156 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5157 }
5158
5159 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5160 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5161 // Prevent clicks in the scrollbars from killing focus
5162 on(node, "mousedown", function () {
5163 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5164 });
5165 node.setAttribute("cm-not-content", "true");
5166 }, function (pos, axis) {
5167 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5168 else { updateScrollTop(cm, pos); }
5169 }, cm);
5170 if (cm.display.scrollbars.addClass)
5171 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5172 }
5173
5174 // Operations are used to wrap a series of changes to the editor
5175 // state in such a way that each change won't have to update the
5176 // cursor and display (which would be awkward, slow, and
5177 // error-prone). Instead, display updates are batched and then all
5178 // combined and executed at once.
5179
5180 var nextOpId = 0;
5181 // Start a new operation.
5182 function startOperation(cm) {
5183 cm.curOp = {
5184 cm: cm,
5185 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5186 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5187 forceUpdate: false, // Used to force a redraw
5188 updateInput: null, // Whether to reset the input textarea
5189 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5190 changeObjs: null, // Accumulated changes, for firing change events
5191 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5192 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5193 selectionChanged: false, // Whether the selection needs to be redrawn
5194 updateMaxLine: false, // Set when the widest line needs to be determined anew
5195 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5196 scrollToPos: null, // Used to scroll to a specific position
5197 focus: false,
5198 id: ++nextOpId // Unique ID
5199 };
5200 pushOperation(cm.curOp);
5201 }
5202
5203 // Finish an operation, updating the display and signalling delayed events
5204 function endOperation(cm) {
5205 var op = cm.curOp;
5206 finishOperation(op, function (group) {
5207 for (var i = 0; i < group.ops.length; i++)
5208 { group.ops[i].cm.curOp = null; }
5209 endOperations(group);
5210 });
5211 }
5212
5213 // The DOM updates done when an operation finishes are batched so
5214 // that the minimum number of relayouts are required.
5215 function endOperations(group) {
5216 var ops = group.ops;
5217 for (var i = 0; i < ops.length; i++) // Read DOM
5218 { endOperation_R1(ops[i]); }
5219 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5220 { endOperation_W1(ops[i$1]); }
5221 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5222 { endOperation_R2(ops[i$2]); }
5223 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5224 { endOperation_W2(ops[i$3]); }
5225 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5226 { endOperation_finish(ops[i$4]); }
5227 }
5228
5229 function endOperation_R1(op) {
5230 var cm = op.cm, display = cm.display;
5231 maybeClipScrollbars(cm);
5232 if (op.updateMaxLine) { findMaxLine(cm); }
5233
5234 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5235 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5236 op.scrollToPos.to.line >= display.viewTo) ||
5237 display.maxLineChanged && cm.options.lineWrapping;
5238 op.update = op.mustUpdate &&
5239 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5240 }
5241
5242 function endOperation_W1(op) {
5243 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5244 }
5245
5246 function endOperation_R2(op) {
5247 var cm = op.cm, display = cm.display;
5248 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5249
5250 op.barMeasure = measureForScrollbars(cm);
5251
5252 // If the max line changed since it was last measured, measure it,
5253 // and ensure the document's width matches it.
5254 // updateDisplay_W2 will use these properties to do the actual resizing
5255 if (display.maxLineChanged && !cm.options.lineWrapping) {
5256 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5257 cm.display.sizerWidth = op.adjustWidthTo;
5258 op.barMeasure.scrollWidth =
5259 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5260 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5261 }
5262
5263 if (op.updatedDisplay || op.selectionChanged)
5264 { op.preparedSelection = display.input.prepareSelection(); }
5265 }
5266
5267 function endOperation_W2(op) {
5268 var cm = op.cm;
5269
5270 if (op.adjustWidthTo != null) {
5271 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5272 if (op.maxScrollLeft < cm.doc.scrollLeft)
5273 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5274 cm.display.maxLineChanged = false;
5275 }
5276
5277 var takeFocus = op.focus && op.focus == activeElt();
5278 if (op.preparedSelection)
5279 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5280 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5281 { updateScrollbars(cm, op.barMeasure); }
5282 if (op.updatedDisplay)
5283 { setDocumentHeight(cm, op.barMeasure); }
5284
5285 if (op.selectionChanged) { restartBlink(cm); }
5286
5287 if (cm.state.focused && op.updateInput)
5288 { cm.display.input.reset(op.typing); }
5289 if (takeFocus) { ensureFocus(op.cm); }
5290 }
5291
5292 function endOperation_finish(op) {
5293 var cm = op.cm, display = cm.display, doc = cm.doc;
5294
5295 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5296
5297 // Abort mouse wheel delta measurement, when scrolling explicitly
5298 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5299 { display.wheelStartX = display.wheelStartY = null; }
5300
5301 // Propagate the scroll position to the actual DOM scroller
5302 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5303
5304 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5305 // If we need to scroll a specific position into view, do so.
5306 if (op.scrollToPos) {
5307 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5308 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5309 maybeScrollWindow(cm, rect);
5310 }
5311
5312 // Fire events for markers that are hidden/unidden by editing or
5313 // undoing
5314 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5315 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5316 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5317 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5318 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5319
5320 if (display.wrapper.offsetHeight)
5321 { doc.scrollTop = cm.display.scroller.scrollTop; }
5322
5323 // Fire change events, and delayed event handlers
5324 if (op.changeObjs)
5325 { signal(cm, "changes", cm, op.changeObjs); }
5326 if (op.update)
5327 { op.update.finish(); }
5328 }
5329
5330 // Run the given function in an operation
5331 function runInOp(cm, f) {
5332 if (cm.curOp) { return f() }
5333 startOperation(cm);
5334 try { return f() }
5335 finally { endOperation(cm); }
5336 }
5337 // Wraps a function in an operation. Returns the wrapped function.
5338 function operation(cm, f) {
5339 return function() {
5340 if (cm.curOp) { return f.apply(cm, arguments) }
5341 startOperation(cm);
5342 try { return f.apply(cm, arguments) }
5343 finally { endOperation(cm); }
5344 }
5345 }
5346 // Used to add methods to editor and doc instances, wrapping them in
5347 // operations.
5348 function methodOp(f) {
5349 return function() {
5350 if (this.curOp) { return f.apply(this, arguments) }
5351 startOperation(this);
5352 try { return f.apply(this, arguments) }
5353 finally { endOperation(this); }
5354 }
5355 }
5356 function docMethodOp(f) {
5357 return function() {
5358 var cm = this.cm;
5359 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5360 startOperation(cm);
5361 try { return f.apply(this, arguments) }
5362 finally { endOperation(cm); }
5363 }
5364 }
5365
5366 // Updates the display.view data structure for a given change to the
5367 // document. From and to are in pre-change coordinates. Lendiff is
5368 // the amount of lines added or subtracted by the change. This is
5369 // used for changes that span multiple lines, or change the way
5370 // lines are divided into visual lines. regLineChange (below)
5371 // registers single-line changes.
5372 function regChange(cm, from, to, lendiff) {
5373 if (from == null) { from = cm.doc.first; }
5374 if (to == null) { to = cm.doc.first + cm.doc.size; }
5375 if (!lendiff) { lendiff = 0; }
5376
5377 var display = cm.display;
5378 if (lendiff && to < display.viewTo &&
5379 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5380 { display.updateLineNumbers = from; }
5381
5382 cm.curOp.viewChanged = true;
5383
5384 if (from >= display.viewTo) { // Change after
5385 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5386 { resetView(cm); }
5387 } else if (to <= display.viewFrom) { // Change before
5388 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5389 resetView(cm);
5390 } else {
5391 display.viewFrom += lendiff;
5392 display.viewTo += lendiff;
5393 }
5394 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5395 resetView(cm);
5396 } else if (from <= display.viewFrom) { // Top overlap
5397 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5398 if (cut) {
5399 display.view = display.view.slice(cut.index);
5400 display.viewFrom = cut.lineN;
5401 display.viewTo += lendiff;
5402 } else {
5403 resetView(cm);
5404 }
5405 } else if (to >= display.viewTo) { // Bottom overlap
5406 var cut$1 = viewCuttingPoint(cm, from, from, -1);
5407 if (cut$1) {
5408 display.view = display.view.slice(0, cut$1.index);
5409 display.viewTo = cut$1.lineN;
5410 } else {
5411 resetView(cm);
5412 }
5413 } else { // Gap in the middle
5414 var cutTop = viewCuttingPoint(cm, from, from, -1);
5415 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5416 if (cutTop && cutBot) {
5417 display.view = display.view.slice(0, cutTop.index)
5418 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5419 .concat(display.view.slice(cutBot.index));
5420 display.viewTo += lendiff;
5421 } else {
5422 resetView(cm);
5423 }
5424 }
5425
5426 var ext = display.externalMeasured;
5427 if (ext) {
5428 if (to < ext.lineN)
5429 { ext.lineN += lendiff; }
5430 else if (from < ext.lineN + ext.size)
5431 { display.externalMeasured = null; }
5432 }
5433 }
5434
5435 // Register a change to a single line. Type must be one of "text",
5436 // "gutter", "class", "widget"
5437 function regLineChange(cm, line, type) {
5438 cm.curOp.viewChanged = true;
5439 var display = cm.display, ext = cm.display.externalMeasured;
5440 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5441 { display.externalMeasured = null; }
5442
5443 if (line < display.viewFrom || line >= display.viewTo) { return }
5444 var lineView = display.view[findViewIndex(cm, line)];
5445 if (lineView.node == null) { return }
5446 var arr = lineView.changes || (lineView.changes = []);
5447 if (indexOf(arr, type) == -1) { arr.push(type); }
5448 }
5449
5450 // Clear the view.
5451 function resetView(cm) {
5452 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5453 cm.display.view = [];
5454 cm.display.viewOffset = 0;
5455 }
5456
5457 function viewCuttingPoint(cm, oldN, newN, dir) {
5458 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5459 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5460 { return {index: index, lineN: newN} }
5461 var n = cm.display.viewFrom;
5462 for (var i = 0; i < index; i++)
5463 { n += view[i].size; }
5464 if (n != oldN) {
5465 if (dir > 0) {
5466 if (index == view.length - 1) { return null }
5467 diff = (n + view[index].size) - oldN;
5468 index++;
5469 } else {
5470 diff = n - oldN;
5471 }
5472 oldN += diff; newN += diff;
5473 }
5474 while (visualLineNo(cm.doc, newN) != newN) {
5475 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
5476 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5477 index += dir;
5478 }
5479 return {index: index, lineN: newN}
5480 }
5481
5482 // Force the view to cover a given range, adding empty view element
5483 // or clipping off existing ones as needed.
5484 function adjustView(cm, from, to) {
5485 var display = cm.display, view = display.view;
5486 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5487 display.view = buildViewArray(cm, from, to);
5488 display.viewFrom = from;
5489 } else {
5490 if (display.viewFrom > from)
5491 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
5492 else if (display.viewFrom < from)
5493 { display.view = display.view.slice(findViewIndex(cm, from)); }
5494 display.viewFrom = from;
5495 if (display.viewTo < to)
5496 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
5497 else if (display.viewTo > to)
5498 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
5499 }
5500 display.viewTo = to;
5501 }
5502
5503 // Count the number of lines in the view whose DOM representation is
5504 // out of date (or nonexistent).
5505 function countDirtyView(cm) {
5506 var view = cm.display.view, dirty = 0;
5507 for (var i = 0; i < view.length; i++) {
5508 var lineView = view[i];
5509 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
5510 }
5511 return dirty
5512 }
5513
5514 // HIGHLIGHT WORKER
5515
5516 function startWorker(cm, time) {
5517 if (cm.doc.highlightFrontier < cm.display.viewTo)
5518 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5519 }
5520
5521 function highlightWorker(cm) {
5522 var doc = cm.doc;
5523 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5524 var end = +new Date + cm.options.workTime;
5525 var context = getContextBefore(cm, doc.highlightFrontier);
5526 var changedLines = [];
5527
5528 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5529 if (context.line >= cm.display.viewFrom) { // Visible
5530 var oldStyles = line.styles;
5531 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5532 var highlighted = highlightLine(cm, line, context, true);
5533 if (resetState) { context.state = resetState; }
5534 line.styles = highlighted.styles;
5535 var oldCls = line.styleClasses, newCls = highlighted.classes;
5536 if (newCls) { line.styleClasses = newCls; }
5537 else if (oldCls) { line.styleClasses = null; }
5538 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5539 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5540 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5541 if (ischange) { changedLines.push(context.line); }
5542 line.stateAfter = context.save();
5543 context.nextLine();
5544 } else {
5545 if (line.text.length <= cm.options.maxHighlightLength)
5546 { processLine(cm, line.text, context); }
5547 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5548 context.nextLine();
5549 }
5550 if (+new Date > end) {
5551 startWorker(cm, cm.options.workDelay);
5552 return true
5553 }
5554 });
5555 doc.highlightFrontier = context.line;
5556 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5557 if (changedLines.length) { runInOp(cm, function () {
5558 for (var i = 0; i < changedLines.length; i++)
5559 { regLineChange(cm, changedLines[i], "text"); }
5560 }); }
5561 }
5562
5563 // DISPLAY DRAWING
5564
5565 var DisplayUpdate = function(cm, viewport, force) {
5566 var display = cm.display;
5567
5568 this.viewport = viewport;
5569 // Store some values that we'll need later (but don't want to force a relayout for)
5570 this.visible = visibleLines(display, cm.doc, viewport);
5571 this.editorIsHidden = !display.wrapper.offsetWidth;
5572 this.wrapperHeight = display.wrapper.clientHeight;
5573 this.wrapperWidth = display.wrapper.clientWidth;
5574 this.oldDisplayWidth = displayWidth(cm);
5575 this.force = force;
5576 this.dims = getDimensions(cm);
5577 this.events = [];
5578 };
5579
5580 DisplayUpdate.prototype.signal = function (emitter, type) {
5581 if (hasHandler(emitter, type))
5582 { this.events.push(arguments); }
5583 };
5584 DisplayUpdate.prototype.finish = function () {
5585 var this$1 = this;
5586
5587 for (var i = 0; i < this.events.length; i++)
5588 { signal.apply(null, this$1.events[i]); }
5589 };
5590
5591 function maybeClipScrollbars(cm) {
5592 var display = cm.display;
5593 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5594 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5595 display.heightForcer.style.height = scrollGap(cm) + "px";
5596 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5597 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5598 display.scrollbarsClipped = true;
5599 }
5600 }
5601
5602 function selectionSnapshot(cm) {
5603 if (cm.hasFocus()) { return null }
5604 var active = activeElt();
5605 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5606 var result = {activeElt: active};
5607 if (window.getSelection) {
5608 var sel = window.getSelection();
5609 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5610 result.anchorNode = sel.anchorNode;
5611 result.anchorOffset = sel.anchorOffset;
5612 result.focusNode = sel.focusNode;
5613 result.focusOffset = sel.focusOffset;
5614 }
5615 }
5616 return result
5617 }
5618
5619 function restoreSelection(snapshot) {
5620 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5621 snapshot.activeElt.focus();
5622 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5623 var sel = window.getSelection(), range$$1 = document.createRange();
5624 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5625 range$$1.collapse(false);
5626 sel.removeAllRanges();
5627 sel.addRange(range$$1);
5628 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5629 }
5630 }
5631
5632 // Does the actual updating of the line display. Bails out
5633 // (returning false) when there is nothing to be done and forced is
5634 // false.
5635 function updateDisplayIfNeeded(cm, update) {
5636 var display = cm.display, doc = cm.doc;
5637
5638 if (update.editorIsHidden) {
5639 resetView(cm);
5640 return false
5641 }
5642
5643 // Bail out if the visible area is already rendered and nothing changed.
5644 if (!update.force &&
5645 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5646 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5647 display.renderedView == display.view && countDirtyView(cm) == 0)
5648 { return false }
5649
5650 if (maybeUpdateLineNumberWidth(cm)) {
5651 resetView(cm);
5652 update.dims = getDimensions(cm);
5653 }
5654
5655 // Compute a suitable new viewport (from & to)
5656 var end = doc.first + doc.size;
5657 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5658 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5659 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5660 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5661 if (sawCollapsedSpans) {
5662 from = visualLineNo(cm.doc, from);
5663 to = visualLineEndNo(cm.doc, to);
5664 }
5665
5666 var different = from != display.viewFrom || to != display.viewTo ||
5667 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5668 adjustView(cm, from, to);
5669
5670 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5671 // Position the mover div to align with the current scroll position
5672 cm.display.mover.style.top = display.viewOffset + "px";
5673
5674 var toUpdate = countDirtyView(cm);
5675 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5676 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5677 { return false }
5678
5679 // For big changes, we hide the enclosing element during the
5680 // update, since that speeds up the operations on most browsers.
5681 var selSnapshot = selectionSnapshot(cm);
5682 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5683 patchDisplay(cm, display.updateLineNumbers, update.dims);
5684 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5685 display.renderedView = display.view;
5686 // There might have been a widget with a focused element that got
5687 // hidden or updated, if so re-focus it.
5688 restoreSelection(selSnapshot);
5689
5690 // Prevent selection and cursors from interfering with the scroll
5691 // width and height.
5692 removeChildren(display.cursorDiv);
5693 removeChildren(display.selectionDiv);
5694 display.gutters.style.height = display.sizer.style.minHeight = 0;
5695
5696 if (different) {
5697 display.lastWrapHeight = update.wrapperHeight;
5698 display.lastWrapWidth = update.wrapperWidth;
5699 startWorker(cm, 400);
5700 }
5701
5702 display.updateLineNumbers = null;
5703
5704 return true
5705 }
5706
5707 function postUpdateDisplay(cm, update) {
5708 var viewport = update.viewport;
5709
5710 for (var first = true;; first = false) {
5711 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5712 // Clip forced viewport to actual scrollable area.
5713 if (viewport && viewport.top != null)
5714 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5715 // Updated line heights might result in the drawn area not
5716 // actually covering the viewport. Keep looping until it does.
5717 update.visible = visibleLines(cm.display, cm.doc, viewport);
5718 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5719 { break }
5720 }
5721 if (!updateDisplayIfNeeded(cm, update)) { break }
5722 updateHeightsInViewport(cm);
5723 var barMeasure = measureForScrollbars(cm);
5724 updateSelection(cm);
5725 updateScrollbars(cm, barMeasure);
5726 setDocumentHeight(cm, barMeasure);
5727 update.force = false;
5728 }
5729
5730 update.signal(cm, "update", cm);
5731 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5732 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5733 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5734 }
5735 }
5736
5737 function updateDisplaySimple(cm, viewport) {
5738 var update = new DisplayUpdate(cm, viewport);
5739 if (updateDisplayIfNeeded(cm, update)) {
5740 updateHeightsInViewport(cm);
5741 postUpdateDisplay(cm, update);
5742 var barMeasure = measureForScrollbars(cm);
5743 updateSelection(cm);
5744 updateScrollbars(cm, barMeasure);
5745 setDocumentHeight(cm, barMeasure);
5746 update.finish();
5747 }
5748 }
5749
5750 // Sync the actual display DOM structure with display.view, removing
5751 // nodes for lines that are no longer in view, and creating the ones
5752 // that are not there yet, and updating the ones that are out of
5753 // date.
5754 function patchDisplay(cm, updateNumbersFrom, dims) {
5755 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5756 var container = display.lineDiv, cur = container.firstChild;
5757
5758 function rm(node) {
5759 var next = node.nextSibling;
5760 // Works around a throw-scroll bug in OS X Webkit
5761 if (webkit && mac && cm.display.currentWheelTarget == node)
5762 { node.style.display = "none"; }
5763 else
5764 { node.parentNode.removeChild(node); }
5765 return next
5766 }
5767
5768 var view = display.view, lineN = display.viewFrom;
5769 // Loop over the elements in the view, syncing cur (the DOM nodes
5770 // in display.lineDiv) with the view as we go.
5771 for (var i = 0; i < view.length; i++) {
5772 var lineView = view[i];
5773 if (lineView.hidden) {
5774 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5775 var node = buildLineElement(cm, lineView, lineN, dims);
5776 container.insertBefore(node, cur);
5777 } else { // Already drawn
5778 while (cur != lineView.node) { cur = rm(cur); }
5779 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5780 updateNumbersFrom <= lineN && lineView.lineNumber;
5781 if (lineView.changes) {
5782 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5783 updateLineForChanges(cm, lineView, lineN, dims);
5784 }
5785 if (updateNumber) {
5786 removeChildren(lineView.lineNumber);
5787 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5788 }
5789 cur = lineView.node.nextSibling;
5790 }
5791 lineN += lineView.size;
5792 }
5793 while (cur) { cur = rm(cur); }
5794 }
5795
5796 function updateGutterSpace(cm) {
5797 var width = cm.display.gutters.offsetWidth;
5798 cm.display.sizer.style.marginLeft = width + "px";
5799 }
5800
5801 function setDocumentHeight(cm, measure) {
5802 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5803 cm.display.heightForcer.style.top = measure.docHeight + "px";
5804 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5805 }
5806
5807 // Rebuild the gutter elements, ensure the margin to the left of the
5808 // code matches their width.
5809 function updateGutters(cm) {
5810 var gutters = cm.display.gutters, specs = cm.options.gutters;
5811 removeChildren(gutters);
5812 var i = 0;
5813 for (; i < specs.length; ++i) {
5814 var gutterClass = specs[i];
5815 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
5816 if (gutterClass == "CodeMirror-linenumbers") {
5817 cm.display.lineGutter = gElt;
5818 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
5819 }
5820 }
5821 gutters.style.display = i ? "" : "none";
5822 updateGutterSpace(cm);
5823 }
5824
5825 // Make sure the gutters options contains the element
5826 // "CodeMirror-linenumbers" when the lineNumbers option is true.
5827 function setGuttersForLineNumbers(options) {
5828 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
5829 if (found == -1 && options.lineNumbers) {
5830 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
5831 } else if (found > -1 && !options.lineNumbers) {
5832 options.gutters = options.gutters.slice(0);
5833 options.gutters.splice(found, 1);
5834 }
5835 }
5836
5837 // Since the delta values reported on mouse wheel events are
5838 // unstandardized between browsers and even browser versions, and
5839 // generally horribly unpredictable, this code starts by measuring
5840 // the scroll effect that the first few mouse wheel events have,
5841 // and, from that, detects the way it can convert deltas to pixel
5842 // offsets afterwards.
5843 //
5844 // The reason we want to know the amount a wheel event will scroll
5845 // is that it gives us a chance to update the display before the
5846 // actual scrolling happens, reducing flickering.
5847
5848 var wheelSamples = 0;
5849 var wheelPixelsPerUnit = null;
5850 // Fill in a browser-detected starting value on browsers where we
5851 // know one. These don't have to be accurate -- the result of them
5852 // being wrong would just be a slight flicker on the first wheel
5853 // scroll (if it is large enough).
5854 if (ie) { wheelPixelsPerUnit = -.53; }
5855 else if (gecko) { wheelPixelsPerUnit = 15; }
5856 else if (chrome) { wheelPixelsPerUnit = -.7; }
5857 else if (safari) { wheelPixelsPerUnit = -1/3; }
5858
5859 function wheelEventDelta(e) {
5860 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
5861 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
5862 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
5863 else if (dy == null) { dy = e.wheelDelta; }
5864 return {x: dx, y: dy}
5865 }
5866 function wheelEventPixels(e) {
5867 var delta = wheelEventDelta(e);
5868 delta.x *= wheelPixelsPerUnit;
5869 delta.y *= wheelPixelsPerUnit;
5870 return delta
5871 }
5872
5873 function onScrollWheel(cm, e) {
5874 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
5875
5876 var display = cm.display, scroll = display.scroller;
5877 // Quit if there's nothing to scroll here
5878 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
5879 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
5880 if (!(dx && canScrollX || dy && canScrollY)) { return }
5881
5882 // Webkit browsers on OS X abort momentum scrolls when the target
5883 // of the scroll event is removed from the scrollable element.
5884 // This hack (see related code in patchDisplay) makes sure the
5885 // element is kept around.
5886 if (dy && mac && webkit) {
5887 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
5888 for (var i = 0; i < view.length; i++) {
5889 if (view[i].node == cur) {
5890 cm.display.currentWheelTarget = cur;
5891 break outer
5892 }
5893 }
5894 }
5895 }
5896
5897 // On some browsers, horizontal scrolling will cause redraws to
5898 // happen before the gutter has been realigned, causing it to
5899 // wriggle around in a most unseemly way. When we have an
5900 // estimated pixels/delta value, we just handle horizontal
5901 // scrolling entirely here. It'll be slightly off from native, but
5902 // better than glitching out.
5903 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
5904 if (dy && canScrollY)
5905 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
5906 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
5907 // Only prevent default scrolling if vertical scrolling is
5908 // actually possible. Otherwise, it causes vertical scroll
5909 // jitter on OSX trackpads when deltaX is small and deltaY
5910 // is large (issue #3579)
5911 if (!dy || (dy && canScrollY))
5912 { e_preventDefault(e); }
5913 display.wheelStartX = null; // Abort measurement, if in progress
5914 return
5915 }
5916
5917 // 'Project' the visible viewport to cover the area that is being
5918 // scrolled into view (if we know enough to estimate it).
5919 if (dy && wheelPixelsPerUnit != null) {
5920 var pixels = dy * wheelPixelsPerUnit;
5921 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
5922 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
5923 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
5924 updateDisplaySimple(cm, {top: top, bottom: bot});
5925 }
5926
5927 if (wheelSamples < 20) {
5928 if (display.wheelStartX == null) {
5929 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
5930 display.wheelDX = dx; display.wheelDY = dy;
5931 setTimeout(function () {
5932 if (display.wheelStartX == null) { return }
5933 var movedX = scroll.scrollLeft - display.wheelStartX;
5934 var movedY = scroll.scrollTop - display.wheelStartY;
5935 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
5936 (movedX && display.wheelDX && movedX / display.wheelDX);
5937 display.wheelStartX = display.wheelStartY = null;
5938 if (!sample) { return }
5939 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
5940 ++wheelSamples;
5941 }, 200);
5942 } else {
5943 display.wheelDX += dx; display.wheelDY += dy;
5944 }
5945 }
5946 }
5947
5948 // Selection objects are immutable. A new one is created every time
5949 // the selection changes. A selection is one or more non-overlapping
5950 // (and non-touching) ranges, sorted, and an integer that indicates
5951 // which one is the primary selection (the one that's scrolled into
5952 // view, that getCursor returns, etc).
5953 var Selection = function(ranges, primIndex) {
5954 this.ranges = ranges;
5955 this.primIndex = primIndex;
5956 };
5957
5958 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
5959
5960 Selection.prototype.equals = function (other) {
5961 var this$1 = this;
5962
5963 if (other == this) { return true }
5964 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
5965 for (var i = 0; i < this.ranges.length; i++) {
5966 var here = this$1.ranges[i], there = other.ranges[i];
5967 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
5968 }
5969 return true
5970 };
5971
5972 Selection.prototype.deepCopy = function () {
5973 var this$1 = this;
5974
5975 var out = [];
5976 for (var i = 0; i < this.ranges.length; i++)
5977 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
5978 return new Selection(out, this.primIndex)
5979 };
5980
5981 Selection.prototype.somethingSelected = function () {
5982 var this$1 = this;
5983
5984 for (var i = 0; i < this.ranges.length; i++)
5985 { if (!this$1.ranges[i].empty()) { return true } }
5986 return false
5987 };
5988
5989 Selection.prototype.contains = function (pos, end) {
5990 var this$1 = this;
5991
5992 if (!end) { end = pos; }
5993 for (var i = 0; i < this.ranges.length; i++) {
5994 var range = this$1.ranges[i];
5995 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
5996 { return i }
5997 }
5998 return -1
5999 };
6000
6001 var Range = function(anchor, head) {
6002 this.anchor = anchor; this.head = head;
6003 };
6004
6005 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6006 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6007 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6008
6009 // Take an unsorted, potentially overlapping set of ranges, and
6010 // build a selection out of it. 'Consumes' ranges array (modifying
6011 // it).
6012 function normalizeSelection(ranges, primIndex) {
6013 var prim = ranges[primIndex];
6014 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6015 primIndex = indexOf(ranges, prim);
6016 for (var i = 1; i < ranges.length; i++) {
6017 var cur = ranges[i], prev = ranges[i - 1];
6018 if (cmp(prev.to(), cur.from()) >= 0) {
6019 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6020 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6021 if (i <= primIndex) { --primIndex; }
6022 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6023 }
6024 }
6025 return new Selection(ranges, primIndex)
6026 }
6027
6028 function simpleSelection(anchor, head) {
6029 return new Selection([new Range(anchor, head || anchor)], 0)
6030 }
6031
6032 // Compute the position of the end of a change (its 'to' property
6033 // refers to the pre-change end).
6034 function changeEnd(change) {
6035 if (!change.text) { return change.to }
6036 return Pos(change.from.line + change.text.length - 1,
6037 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6038 }
6039
6040 // Adjust a position to refer to the post-change position of the
6041 // same text, or the end of the change if the change covers it.
6042 function adjustForChange(pos, change) {
6043 if (cmp(pos, change.from) < 0) { return pos }
6044 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6045
6046 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6047 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6048 return Pos(line, ch)
6049 }
6050
6051 function computeSelAfterChange(doc, change) {
6052 var out = [];
6053 for (var i = 0; i < doc.sel.ranges.length; i++) {
6054 var range = doc.sel.ranges[i];
6055 out.push(new Range(adjustForChange(range.anchor, change),
6056 adjustForChange(range.head, change)));
6057 }
6058 return normalizeSelection(out, doc.sel.primIndex)
6059 }
6060
6061 function offsetPos(pos, old, nw) {
6062 if (pos.line == old.line)
6063 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6064 else
6065 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6066 }
6067
6068 // Used by replaceSelections to allow moving the selection to the
6069 // start or around the replaced test. Hint may be "start" or "around".
6070 function computeReplacedSel(doc, changes, hint) {
6071 var out = [];
6072 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6073 for (var i = 0; i < changes.length; i++) {
6074 var change = changes[i];
6075 var from = offsetPos(change.from, oldPrev, newPrev);
6076 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6077 oldPrev = change.to;
6078 newPrev = to;
6079 if (hint == "around") {
6080 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6081 out[i] = new Range(inv ? to : from, inv ? from : to);
6082 } else {
6083 out[i] = new Range(from, from);
6084 }
6085 }
6086 return new Selection(out, doc.sel.primIndex)
6087 }
6088
6089 // Used to get the editor into a consistent state again when options change.
6090
6091 function loadMode(cm) {
6092 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6093 resetModeState(cm);
6094 }
6095
6096 function resetModeState(cm) {
6097 cm.doc.iter(function (line) {
6098 if (line.stateAfter) { line.stateAfter = null; }
6099 if (line.styles) { line.styles = null; }
6100 });
6101 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6102 startWorker(cm, 100);
6103 cm.state.modeGen++;
6104 if (cm.curOp) { regChange(cm); }
6105 }
6106
6107 // DOCUMENT DATA STRUCTURE
6108
6109 // By default, updates that start and end at the beginning of a line
6110 // are treated specially, in order to make the association of line
6111 // widgets and marker elements with the text behave more intuitive.
6112 function isWholeLineUpdate(doc, change) {
6113 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6114 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6115 }
6116
6117 // Perform a change on the document data structure.
6118 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6119 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6120 function update(line, text, spans) {
6121 updateLine(line, text, spans, estimateHeight$$1);
6122 signalLater(line, "change", line, change);
6123 }
6124 function linesFor(start, end) {
6125 var result = [];
6126 for (var i = start; i < end; ++i)
6127 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6128 return result
6129 }
6130
6131 var from = change.from, to = change.to, text = change.text;
6132 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6133 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6134
6135 // Adjust the line structure
6136 if (change.full) {
6137 doc.insert(0, linesFor(0, text.length));
6138 doc.remove(text.length, doc.size - text.length);
6139 } else if (isWholeLineUpdate(doc, change)) {
6140 // This is a whole-line replace. Treated specially to make
6141 // sure line objects move the way they are supposed to.
6142 var added = linesFor(0, text.length - 1);
6143 update(lastLine, lastLine.text, lastSpans);
6144 if (nlines) { doc.remove(from.line, nlines); }
6145 if (added.length) { doc.insert(from.line, added); }
6146 } else if (firstLine == lastLine) {
6147 if (text.length == 1) {
6148 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6149 } else {
6150 var added$1 = linesFor(1, text.length - 1);
6151 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6152 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6153 doc.insert(from.line + 1, added$1);
6154 }
6155 } else if (text.length == 1) {
6156 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6157 doc.remove(from.line + 1, nlines);
6158 } else {
6159 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6160 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6161 var added$2 = linesFor(1, text.length - 1);
6162 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6163 doc.insert(from.line + 1, added$2);
6164 }
6165
6166 signalLater(doc, "change", doc, change);
6167 }
6168
6169 // Call f for all linked documents.
6170 function linkedDocs(doc, f, sharedHistOnly) {
6171 function propagate(doc, skip, sharedHist) {
6172 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6173 var rel = doc.linked[i];
6174 if (rel.doc == skip) { continue }
6175 var shared = sharedHist && rel.sharedHist;
6176 if (sharedHistOnly && !shared) { continue }
6177 f(rel.doc, shared);
6178 propagate(rel.doc, doc, shared);
6179 } }
6180 }
6181 propagate(doc, null, true);
6182 }
6183
6184 // Attach a document to an editor.
6185 function attachDoc(cm, doc) {
6186 if (doc.cm) { throw new Error("This document is already in use.") }
6187 cm.doc = doc;
6188 doc.cm = cm;
6189 estimateLineHeights(cm);
6190 loadMode(cm);
6191 setDirectionClass(cm);
6192 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6193 cm.options.mode = doc.modeOption;
6194 regChange(cm);
6195 }
6196
6197 function setDirectionClass(cm) {
6198 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6199 }
6200
6201 function directionChanged(cm) {
6202 runInOp(cm, function () {
6203 setDirectionClass(cm);
6204 regChange(cm);
6205 });
6206 }
6207
6208 function History(startGen) {
6209 // Arrays of change events and selections. Doing something adds an
6210 // event to done and clears undo. Undoing moves events from done
6211 // to undone, redoing moves them in the other direction.
6212 this.done = []; this.undone = [];
6213 this.undoDepth = Infinity;
6214 // Used to track when changes can be merged into a single undo
6215 // event
6216 this.lastModTime = this.lastSelTime = 0;
6217 this.lastOp = this.lastSelOp = null;
6218 this.lastOrigin = this.lastSelOrigin = null;
6219 // Used by the isClean() method
6220 this.generation = this.maxGeneration = startGen || 1;
6221 }
6222
6223 // Create a history change event from an updateDoc-style change
6224 // object.
6225 function historyChangeFromChange(doc, change) {
6226 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6227 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6228 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6229 return histChange
6230 }
6231
6232 // Pop all selection events off the end of a history array. Stop at
6233 // a change event.
6234 function clearSelectionEvents(array) {
6235 while (array.length) {
6236 var last = lst(array);
6237 if (last.ranges) { array.pop(); }
6238 else { break }
6239 }
6240 }
6241
6242 // Find the top change event in the history. Pop off selection
6243 // events that are in the way.
6244 function lastChangeEvent(hist, force) {
6245 if (force) {
6246 clearSelectionEvents(hist.done);
6247 return lst(hist.done)
6248 } else if (hist.done.length && !lst(hist.done).ranges) {
6249 return lst(hist.done)
6250 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6251 hist.done.pop();
6252 return lst(hist.done)
6253 }
6254 }
6255
6256 // Register a change in the history. Merges changes that are within
6257 // a single operation, or are close together with an origin that
6258 // allows merging (starting with "+") into a single event.
6259 function addChangeToHistory(doc, change, selAfter, opId) {
6260 var hist = doc.history;
6261 hist.undone.length = 0;
6262 var time = +new Date, cur;
6263 var last;
6264
6265 if ((hist.lastOp == opId ||
6266 hist.lastOrigin == change.origin && change.origin &&
6267 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
6268 change.origin.charAt(0) == "*")) &&
6269 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6270 // Merge this change into the last event
6271 last = lst(cur.changes);
6272 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6273 // Optimized case for simple insertion -- don't want to add
6274 // new changesets for every character typed
6275 last.to = changeEnd(change);
6276 } else {
6277 // Add new sub-event
6278 cur.changes.push(historyChangeFromChange(doc, change));
6279 }
6280 } else {
6281 // Can not be merged, start a new event.
6282 var before = lst(hist.done);
6283 if (!before || !before.ranges)
6284 { pushSelectionToHistory(doc.sel, hist.done); }
6285 cur = {changes: [historyChangeFromChange(doc, change)],
6286 generation: hist.generation};
6287 hist.done.push(cur);
6288 while (hist.done.length > hist.undoDepth) {
6289 hist.done.shift();
6290 if (!hist.done[0].ranges) { hist.done.shift(); }
6291 }
6292 }
6293 hist.done.push(selAfter);
6294 hist.generation = ++hist.maxGeneration;
6295 hist.lastModTime = hist.lastSelTime = time;
6296 hist.lastOp = hist.lastSelOp = opId;
6297 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6298
6299 if (!last) { signal(doc, "historyAdded"); }
6300 }
6301
6302 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6303 var ch = origin.charAt(0);
6304 return ch == "*" ||
6305 ch == "+" &&
6306 prev.ranges.length == sel.ranges.length &&
6307 prev.somethingSelected() == sel.somethingSelected() &&
6308 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6309 }
6310
6311 // Called whenever the selection changes, sets the new selection as
6312 // the pending selection in the history, and pushes the old pending
6313 // selection into the 'done' array when it was significantly
6314 // different (in number of selected ranges, emptiness, or time).
6315 function addSelectionToHistory(doc, sel, opId, options) {
6316 var hist = doc.history, origin = options && options.origin;
6317
6318 // A new event is started when the previous origin does not match
6319 // the current, or the origins don't allow matching. Origins
6320 // starting with * are always merged, those starting with + are
6321 // merged when similar and close together in time.
6322 if (opId == hist.lastSelOp ||
6323 (origin && hist.lastSelOrigin == origin &&
6324 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6325 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6326 { hist.done[hist.done.length - 1] = sel; }
6327 else
6328 { pushSelectionToHistory(sel, hist.done); }
6329
6330 hist.lastSelTime = +new Date;
6331 hist.lastSelOrigin = origin;
6332 hist.lastSelOp = opId;
6333 if (options && options.clearRedo !== false)
6334 { clearSelectionEvents(hist.undone); }
6335 }
6336
6337 function pushSelectionToHistory(sel, dest) {
6338 var top = lst(dest);
6339 if (!(top && top.ranges && top.equals(sel)))
6340 { dest.push(sel); }
6341 }
6342
6343 // Used to store marked span information in the history.
6344 function attachLocalSpans(doc, change, from, to) {
6345 var existing = change["spans_" + doc.id], n = 0;
6346 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6347 if (line.markedSpans)
6348 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6349 ++n;
6350 });
6351 }
6352
6353 // When un/re-doing restores text containing marked spans, those
6354 // that have been explicitly cleared should not be restored.
6355 function removeClearedSpans(spans) {
6356 if (!spans) { return null }
6357 var out;
6358 for (var i = 0; i < spans.length; ++i) {
6359 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6360 else if (out) { out.push(spans[i]); }
6361 }
6362 return !out ? spans : out.length ? out : null
6363 }
6364
6365 // Retrieve and filter the old marked spans stored in a change event.
6366 function getOldSpans(doc, change) {
6367 var found = change["spans_" + doc.id];
6368 if (!found) { return null }
6369 var nw = [];
6370 for (var i = 0; i < change.text.length; ++i)
6371 { nw.push(removeClearedSpans(found[i])); }
6372 return nw
6373 }
6374
6375 // Used for un/re-doing changes from the history. Combines the
6376 // result of computing the existing spans with the set of spans that
6377 // existed in the history (so that deleting around a span and then
6378 // undoing brings back the span).
6379 function mergeOldSpans(doc, change) {
6380 var old = getOldSpans(doc, change);
6381 var stretched = stretchSpansOverChange(doc, change);
6382 if (!old) { return stretched }
6383 if (!stretched) { return old }
6384
6385 for (var i = 0; i < old.length; ++i) {
6386 var oldCur = old[i], stretchCur = stretched[i];
6387 if (oldCur && stretchCur) {
6388 spans: for (var j = 0; j < stretchCur.length; ++j) {
6389 var span = stretchCur[j];
6390 for (var k = 0; k < oldCur.length; ++k)
6391 { if (oldCur[k].marker == span.marker) { continue spans } }
6392 oldCur.push(span);
6393 }
6394 } else if (stretchCur) {
6395 old[i] = stretchCur;
6396 }
6397 }
6398 return old
6399 }
6400
6401 // Used both to provide a JSON-safe object in .getHistory, and, when
6402 // detaching a document, to split the history in two
6403 function copyHistoryArray(events, newGroup, instantiateSel) {
6404 var copy = [];
6405 for (var i = 0; i < events.length; ++i) {
6406 var event = events[i];
6407 if (event.ranges) {
6408 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6409 continue
6410 }
6411 var changes = event.changes, newChanges = [];
6412 copy.push({changes: newChanges});
6413 for (var j = 0; j < changes.length; ++j) {
6414 var change = changes[j], m = (void 0);
6415 newChanges.push({from: change.from, to: change.to, text: change.text});
6416 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6417 if (indexOf(newGroup, Number(m[1])) > -1) {
6418 lst(newChanges)[prop] = change[prop];
6419 delete change[prop];
6420 }
6421 } } }
6422 }
6423 }
6424 return copy
6425 }
6426
6427 // The 'scroll' parameter given to many of these indicated whether
6428 // the new cursor position should be scrolled into view after
6429 // modifying the selection.
6430
6431 // If shift is held or the extend flag is set, extends a range to
6432 // include a given position (and optionally a second position).
6433 // Otherwise, simply returns the range between the given positions.
6434 // Used for cursor motion and such.
6435 function extendRange(range, head, other, extend) {
6436 if (extend) {
6437 var anchor = range.anchor;
6438 if (other) {
6439 var posBefore = cmp(head, anchor) < 0;
6440 if (posBefore != (cmp(other, anchor) < 0)) {
6441 anchor = head;
6442 head = other;
6443 } else if (posBefore != (cmp(head, other) < 0)) {
6444 head = other;
6445 }
6446 }
6447 return new Range(anchor, head)
6448 } else {
6449 return new Range(other || head, head)
6450 }
6451 }
6452
6453 // Extend the primary selection range, discard the rest.
6454 function extendSelection(doc, head, other, options, extend) {
6455 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6456 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6457 }
6458
6459 // Extend all selections (pos is an array of selections with length
6460 // equal the number of selections)
6461 function extendSelections(doc, heads, options) {
6462 var out = [];
6463 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6464 for (var i = 0; i < doc.sel.ranges.length; i++)
6465 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6466 var newSel = normalizeSelection(out, doc.sel.primIndex);
6467 setSelection(doc, newSel, options);
6468 }
6469
6470 // Updates a single range in the selection.
6471 function replaceOneSelection(doc, i, range, options) {
6472 var ranges = doc.sel.ranges.slice(0);
6473 ranges[i] = range;
6474 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
6475 }
6476
6477 // Reset the selection to a single range.
6478 function setSimpleSelection(doc, anchor, head, options) {
6479 setSelection(doc, simpleSelection(anchor, head), options);
6480 }
6481
6482 // Give beforeSelectionChange handlers a change to influence a
6483 // selection update.
6484 function filterSelectionChange(doc, sel, options) {
6485 var obj = {
6486 ranges: sel.ranges,
6487 update: function(ranges) {
6488 var this$1 = this;
6489
6490 this.ranges = [];
6491 for (var i = 0; i < ranges.length; i++)
6492 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6493 clipPos(doc, ranges[i].head)); }
6494 },
6495 origin: options && options.origin
6496 };
6497 signal(doc, "beforeSelectionChange", doc, obj);
6498 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6499 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
6500 else { return sel }
6501 }
6502
6503 function setSelectionReplaceHistory(doc, sel, options) {
6504 var done = doc.history.done, last = lst(done);
6505 if (last && last.ranges) {
6506 done[done.length - 1] = sel;
6507 setSelectionNoUndo(doc, sel, options);
6508 } else {
6509 setSelection(doc, sel, options);
6510 }
6511 }
6512
6513 // Set a new selection.
6514 function setSelection(doc, sel, options) {
6515 setSelectionNoUndo(doc, sel, options);
6516 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6517 }
6518
6519 function setSelectionNoUndo(doc, sel, options) {
6520 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6521 { sel = filterSelectionChange(doc, sel, options); }
6522
6523 var bias = options && options.bias ||
6524 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6525 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6526
6527 if (!(options && options.scroll === false) && doc.cm)
6528 { ensureCursorVisible(doc.cm); }
6529 }
6530
6531 function setSelectionInner(doc, sel) {
6532 if (sel.equals(doc.sel)) { return }
6533
6534 doc.sel = sel;
6535
6536 if (doc.cm) {
6537 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
6538 signalCursorActivity(doc.cm);
6539 }
6540 signalLater(doc, "cursorActivity", doc);
6541 }
6542
6543 // Verify that the selection does not partially select any atomic
6544 // marked ranges.
6545 function reCheckSelection(doc) {
6546 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6547 }
6548
6549 // Return a selection that does not partially select any atomic
6550 // ranges.
6551 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6552 var out;
6553 for (var i = 0; i < sel.ranges.length; i++) {
6554 var range = sel.ranges[i];
6555 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6556 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6557 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6558 if (out || newAnchor != range.anchor || newHead != range.head) {
6559 if (!out) { out = sel.ranges.slice(0, i); }
6560 out[i] = new Range(newAnchor, newHead);
6561 }
6562 }
6563 return out ? normalizeSelection(out, sel.primIndex) : sel
6564 }
6565
6566 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6567 var line = getLine(doc, pos.line);
6568 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6569 var sp = line.markedSpans[i], m = sp.marker;
6570 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6571 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6572 if (mayClear) {
6573 signal(m, "beforeCursorEnter");
6574 if (m.explicitlyCleared) {
6575 if (!line.markedSpans) { break }
6576 else {--i; continue}
6577 }
6578 }
6579 if (!m.atomic) { continue }
6580
6581 if (oldPos) {
6582 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6583 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6584 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6585 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6586 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6587 }
6588
6589 var far = m.find(dir < 0 ? -1 : 1);
6590 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6591 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6592 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6593 }
6594 } }
6595 return pos
6596 }
6597
6598 // Ensure a given position is not inside an atomic range.
6599 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6600 var dir = bias || 1;
6601 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6602 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6603 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6604 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6605 if (!found) {
6606 doc.cantEdit = true;
6607 return Pos(doc.first, 0)
6608 }
6609 return found
6610 }
6611
6612 function movePos(doc, pos, dir, line) {
6613 if (dir < 0 && pos.ch == 0) {
6614 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6615 else { return null }
6616 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6617 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6618 else { return null }
6619 } else {
6620 return new Pos(pos.line, pos.ch + dir)
6621 }
6622 }
6623
6624 function selectAll(cm) {
6625 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6626 }
6627
6628 // UPDATING
6629
6630 // Allow "beforeChange" event handlers to influence a change
6631 function filterChange(doc, change, update) {
6632 var obj = {
6633 canceled: false,
6634 from: change.from,
6635 to: change.to,
6636 text: change.text,
6637 origin: change.origin,
6638 cancel: function () { return obj.canceled = true; }
6639 };
6640 if (update) { obj.update = function (from, to, text, origin) {
6641 if (from) { obj.from = clipPos(doc, from); }
6642 if (to) { obj.to = clipPos(doc, to); }
6643 if (text) { obj.text = text; }
6644 if (origin !== undefined) { obj.origin = origin; }
6645 }; }
6646 signal(doc, "beforeChange", doc, obj);
6647 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6648
6649 if (obj.canceled) { return null }
6650 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6651 }
6652
6653 // Apply a change to a document, and add it to the document's
6654 // history, and propagating it to all linked documents.
6655 function makeChange(doc, change, ignoreReadOnly) {
6656 if (doc.cm) {
6657 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6658 if (doc.cm.state.suppressEdits) { return }
6659 }
6660
6661 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6662 change = filterChange(doc, change, true);
6663 if (!change) { return }
6664 }
6665
6666 // Possibly split or suppress the update based on the presence
6667 // of read-only spans in its range.
6668 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6669 if (split) {
6670 for (var i = split.length - 1; i >= 0; --i)
6671 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6672 } else {
6673 makeChangeInner(doc, change);
6674 }
6675 }
6676
6677 function makeChangeInner(doc, change) {
6678 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6679 var selAfter = computeSelAfterChange(doc, change);
6680 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6681
6682 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6683 var rebased = [];
6684
6685 linkedDocs(doc, function (doc, sharedHist) {
6686 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6687 rebaseHist(doc.history, change);
6688 rebased.push(doc.history);
6689 }
6690 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6691 });
6692 }
6693
6694 // Revert a change stored in a document's history.
6695 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6696 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
6697
6698 var hist = doc.history, event, selAfter = doc.sel;
6699 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6700
6701 // Verify that there is a useable event (so that ctrl-z won't
6702 // needlessly clear selection events)
6703 var i = 0;
6704 for (; i < source.length; i++) {
6705 event = source[i];
6706 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6707 { break }
6708 }
6709 if (i == source.length) { return }
6710 hist.lastOrigin = hist.lastSelOrigin = null;
6711
6712 for (;;) {
6713 event = source.pop();
6714 if (event.ranges) {
6715 pushSelectionToHistory(event, dest);
6716 if (allowSelectionOnly && !event.equals(doc.sel)) {
6717 setSelection(doc, event, {clearRedo: false});
6718 return
6719 }
6720 selAfter = event;
6721 }
6722 else { break }
6723 }
6724
6725 // Build up a reverse change object to add to the opposite history
6726 // stack (redo when undoing, and vice versa).
6727 var antiChanges = [];
6728 pushSelectionToHistory(selAfter, dest);
6729 dest.push({changes: antiChanges, generation: hist.generation});
6730 hist.generation = event.generation || ++hist.maxGeneration;
6731
6732 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6733
6734 var loop = function ( i ) {
6735 var change = event.changes[i];
6736 change.origin = type;
6737 if (filter && !filterChange(doc, change, false)) {
6738 source.length = 0;
6739 return {}
6740 }
6741
6742 antiChanges.push(historyChangeFromChange(doc, change));
6743
6744 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6745 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6746 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6747 var rebased = [];
6748
6749 // Propagate to the linked documents
6750 linkedDocs(doc, function (doc, sharedHist) {
6751 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6752 rebaseHist(doc.history, change);
6753 rebased.push(doc.history);
6754 }
6755 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6756 });
6757 };
6758
6759 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6760 var returned = loop( i$1 );
6761
6762 if ( returned ) return returned.v;
6763 }
6764 }
6765
6766 // Sub-views need their line numbers shifted when text is added
6767 // above or below them in the parent document.
6768 function shiftDoc(doc, distance) {
6769 if (distance == 0) { return }
6770 doc.first += distance;
6771 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6772 Pos(range.anchor.line + distance, range.anchor.ch),
6773 Pos(range.head.line + distance, range.head.ch)
6774 ); }), doc.sel.primIndex);
6775 if (doc.cm) {
6776 regChange(doc.cm, doc.first, doc.first - distance, distance);
6777 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6778 { regLineChange(doc.cm, l, "gutter"); }
6779 }
6780 }
6781
6782 // More lower-level change function, handling only a single document
6783 // (not linked ones).
6784 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6785 if (doc.cm && !doc.cm.curOp)
6786 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6787
6788 if (change.to.line < doc.first) {
6789 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6790 return
6791 }
6792 if (change.from.line > doc.lastLine()) { return }
6793
6794 // Clip the change to the size of this doc
6795 if (change.from.line < doc.first) {
6796 var shift = change.text.length - 1 - (doc.first - change.from.line);
6797 shiftDoc(doc, shift);
6798 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6799 text: [lst(change.text)], origin: change.origin};
6800 }
6801 var last = doc.lastLine();
6802 if (change.to.line > last) {
6803 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6804 text: [change.text[0]], origin: change.origin};
6805 }
6806
6807 change.removed = getBetween(doc, change.from, change.to);
6808
6809 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6810 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6811 else { updateDoc(doc, change, spans); }
6812 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6813 }
6814
6815 // Handle the interaction of a change to a document with the editor
6816 // that this document is part of.
6817 function makeChangeSingleDocInEditor(cm, change, spans) {
6818 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6819
6820 var recomputeMaxLength = false, checkWidthStart = from.line;
6821 if (!cm.options.lineWrapping) {
6822 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6823 doc.iter(checkWidthStart, to.line + 1, function (line) {
6824 if (line == display.maxLine) {
6825 recomputeMaxLength = true;
6826 return true
6827 }
6828 });
6829 }
6830
6831 if (doc.sel.contains(change.from, change.to) > -1)
6832 { signalCursorActivity(cm); }
6833
6834 updateDoc(doc, change, spans, estimateHeight(cm));
6835
6836 if (!cm.options.lineWrapping) {
6837 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
6838 var len = lineLength(line);
6839 if (len > display.maxLineLength) {
6840 display.maxLine = line;
6841 display.maxLineLength = len;
6842 display.maxLineChanged = true;
6843 recomputeMaxLength = false;
6844 }
6845 });
6846 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
6847 }
6848
6849 retreatFrontier(doc, from.line);
6850 startWorker(cm, 400);
6851
6852 var lendiff = change.text.length - (to.line - from.line) - 1;
6853 // Remember that these lines changed, for updating the display
6854 if (change.full)
6855 { regChange(cm); }
6856 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
6857 { regLineChange(cm, from.line, "text"); }
6858 else
6859 { regChange(cm, from.line, to.line + 1, lendiff); }
6860
6861 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
6862 if (changeHandler || changesHandler) {
6863 var obj = {
6864 from: from, to: to,
6865 text: change.text,
6866 removed: change.removed,
6867 origin: change.origin
6868 };
6869 if (changeHandler) { signalLater(cm, "change", cm, obj); }
6870 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
6871 }
6872 cm.display.selForContextMenu = null;
6873 }
6874
6875 function replaceRange(doc, code, from, to, origin) {
6876 if (!to) { to = from; }
6877 if (cmp(to, from) < 0) { var assign;
6878 (assign = [to, from], from = assign[0], to = assign[1], assign); }
6879 if (typeof code == "string") { code = doc.splitLines(code); }
6880 makeChange(doc, {from: from, to: to, text: code, origin: origin});
6881 }
6882
6883 // Rebasing/resetting history to deal with externally-sourced changes
6884
6885 function rebaseHistSelSingle(pos, from, to, diff) {
6886 if (to < pos.line) {
6887 pos.line += diff;
6888 } else if (from < pos.line) {
6889 pos.line = from;
6890 pos.ch = 0;
6891 }
6892 }
6893
6894 // Tries to rebase an array of history events given a change in the
6895 // document. If the change touches the same lines as the event, the
6896 // event, and everything 'behind' it, is discarded. If the change is
6897 // before the event, the event's positions are updated. Uses a
6898 // copy-on-write scheme for the positions, to avoid having to
6899 // reallocate them all on every rebase, but also avoid problems with
6900 // shared position objects being unsafely updated.
6901 function rebaseHistArray(array, from, to, diff) {
6902 for (var i = 0; i < array.length; ++i) {
6903 var sub = array[i], ok = true;
6904 if (sub.ranges) {
6905 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
6906 for (var j = 0; j < sub.ranges.length; j++) {
6907 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
6908 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
6909 }
6910 continue
6911 }
6912 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
6913 var cur = sub.changes[j$1];
6914 if (to < cur.from.line) {
6915 cur.from = Pos(cur.from.line + diff, cur.from.ch);
6916 cur.to = Pos(cur.to.line + diff, cur.to.ch);
6917 } else if (from <= cur.to.line) {
6918 ok = false;
6919 break
6920 }
6921 }
6922 if (!ok) {
6923 array.splice(0, i + 1);
6924 i = 0;
6925 }
6926 }
6927 }
6928
6929 function rebaseHist(hist, change) {
6930 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
6931 rebaseHistArray(hist.done, from, to, diff);
6932 rebaseHistArray(hist.undone, from, to, diff);
6933 }
6934
6935 // Utility for applying a change to a line by handle or number,
6936 // returning the number and optionally registering the line as
6937 // changed.
6938 function changeLine(doc, handle, changeType, op) {
6939 var no = handle, line = handle;
6940 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
6941 else { no = lineNo(handle); }
6942 if (no == null) { return null }
6943 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
6944 return line
6945 }
6946
6947 // The document is represented as a BTree consisting of leaves, with
6948 // chunk of lines in them, and branches, with up to ten leaves or
6949 // other branch nodes below them. The top node is always a branch
6950 // node, and is the document object itself (meaning it has
6951 // additional methods and properties).
6952 //
6953 // All nodes have parent links. The tree is used both to go from
6954 // line numbers to line objects, and to go from objects to numbers.
6955 // It also indexes by height, and is used to convert between height
6956 // and line object, and to find the total height of the document.
6957 //
6958 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
6959
6960 function LeafChunk(lines) {
6961 var this$1 = this;
6962
6963 this.lines = lines;
6964 this.parent = null;
6965 var height = 0;
6966 for (var i = 0; i < lines.length; ++i) {
6967 lines[i].parent = this$1;
6968 height += lines[i].height;
6969 }
6970 this.height = height;
6971 }
6972
6973 LeafChunk.prototype = {
6974 chunkSize: function chunkSize() { return this.lines.length },
6975
6976 // Remove the n lines at offset 'at'.
6977 removeInner: function removeInner(at, n) {
6978 var this$1 = this;
6979
6980 for (var i = at, e = at + n; i < e; ++i) {
6981 var line = this$1.lines[i];
6982 this$1.height -= line.height;
6983 cleanUpLine(line);
6984 signalLater(line, "delete");
6985 }
6986 this.lines.splice(at, n);
6987 },
6988
6989 // Helper used to collapse a small branch into a single leaf.
6990 collapse: function collapse(lines) {
6991 lines.push.apply(lines, this.lines);
6992 },
6993
6994 // Insert the given array of lines at offset 'at', count them as
6995 // having the given height.
6996 insertInner: function insertInner(at, lines, height) {
6997 var this$1 = this;
6998
6999 this.height += height;
7000 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7001 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
7002 },
7003
7004 // Used to iterate over a part of the tree.
7005 iterN: function iterN(at, n, op) {
7006 var this$1 = this;
7007
7008 for (var e = at + n; at < e; ++at)
7009 { if (op(this$1.lines[at])) { return true } }
7010 }
7011 };
7012
7013 function BranchChunk(children) {
7014 var this$1 = this;
7015
7016 this.children = children;
7017 var size = 0, height = 0;
7018 for (var i = 0; i < children.length; ++i) {
7019 var ch = children[i];
7020 size += ch.chunkSize(); height += ch.height;
7021 ch.parent = this$1;
7022 }
7023 this.size = size;
7024 this.height = height;
7025 this.parent = null;
7026 }
7027
7028 BranchChunk.prototype = {
7029 chunkSize: function chunkSize() { return this.size },
7030
7031 removeInner: function removeInner(at, n) {
7032 var this$1 = this;
7033
7034 this.size -= n;
7035 for (var i = 0; i < this.children.length; ++i) {
7036 var child = this$1.children[i], sz = child.chunkSize();
7037 if (at < sz) {
7038 var rm = Math.min(n, sz - at), oldHeight = child.height;
7039 child.removeInner(at, rm);
7040 this$1.height -= oldHeight - child.height;
7041 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
7042 if ((n -= rm) == 0) { break }
7043 at = 0;
7044 } else { at -= sz; }
7045 }
7046 // If the result is smaller than 25 lines, ensure that it is a
7047 // single leaf node.
7048 if (this.size - n < 25 &&
7049 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7050 var lines = [];
7051 this.collapse(lines);
7052 this.children = [new LeafChunk(lines)];
7053 this.children[0].parent = this;
7054 }
7055 },
7056
7057 collapse: function collapse(lines) {
7058 var this$1 = this;
7059
7060 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
7061 },
7062
7063 insertInner: function insertInner(at, lines, height) {
7064 var this$1 = this;
7065
7066 this.size += lines.length;
7067 this.height += height;
7068 for (var i = 0; i < this.children.length; ++i) {
7069 var child = this$1.children[i], sz = child.chunkSize();
7070 if (at <= sz) {
7071 child.insertInner(at, lines, height);
7072 if (child.lines && child.lines.length > 50) {
7073 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7074 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7075 var remaining = child.lines.length % 25 + 25;
7076 for (var pos = remaining; pos < child.lines.length;) {
7077 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7078 child.height -= leaf.height;
7079 this$1.children.splice(++i, 0, leaf);
7080 leaf.parent = this$1;
7081 }
7082 child.lines = child.lines.slice(0, remaining);
7083 this$1.maybeSpill();
7084 }
7085 break
7086 }
7087 at -= sz;
7088 }
7089 },
7090
7091 // When a node has grown, check whether it should be split.
7092 maybeSpill: function maybeSpill() {
7093 if (this.children.length <= 10) { return }
7094 var me = this;
7095 do {
7096 var spilled = me.children.splice(me.children.length - 5, 5);
7097 var sibling = new BranchChunk(spilled);
7098 if (!me.parent) { // Become the parent node
7099 var copy = new BranchChunk(me.children);
7100 copy.parent = me;
7101 me.children = [copy, sibling];
7102 me = copy;
7103 } else {
7104 me.size -= sibling.size;
7105 me.height -= sibling.height;
7106 var myIndex = indexOf(me.parent.children, me);
7107 me.parent.children.splice(myIndex + 1, 0, sibling);
7108 }
7109 sibling.parent = me.parent;
7110 } while (me.children.length > 10)
7111 me.parent.maybeSpill();
7112 },
7113
7114 iterN: function iterN(at, n, op) {
7115 var this$1 = this;
7116
7117 for (var i = 0; i < this.children.length; ++i) {
7118 var child = this$1.children[i], sz = child.chunkSize();
7119 if (at < sz) {
7120 var used = Math.min(n, sz - at);
7121 if (child.iterN(at, used, op)) { return true }
7122 if ((n -= used) == 0) { break }
7123 at = 0;
7124 } else { at -= sz; }
7125 }
7126 }
7127 };
7128
7129 // Line widgets are block elements displayed above or below a line.
7130
7131 var LineWidget = function(doc, node, options) {
7132 var this$1 = this;
7133
7134 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7135 { this$1[opt] = options[opt]; } } }
7136 this.doc = doc;
7137 this.node = node;
7138 };
7139
7140 LineWidget.prototype.clear = function () {
7141 var this$1 = this;
7142
7143 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7144 if (no == null || !ws) { return }
7145 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7146 if (!ws.length) { line.widgets = null; }
7147 var height = widgetHeight(this);
7148 updateLineHeight(line, Math.max(0, line.height - height));
7149 if (cm) {
7150 runInOp(cm, function () {
7151 adjustScrollWhenAboveVisible(cm, line, -height);
7152 regLineChange(cm, no, "widget");
7153 });
7154 signalLater(cm, "lineWidgetCleared", cm, this, no);
7155 }
7156 };
7157
7158 LineWidget.prototype.changed = function () {
7159 var this$1 = this;
7160
7161 var oldH = this.height, cm = this.doc.cm, line = this.line;
7162 this.height = null;
7163 var diff = widgetHeight(this) - oldH;
7164 if (!diff) { return }
7165 updateLineHeight(line, line.height + diff);
7166 if (cm) {
7167 runInOp(cm, function () {
7168 cm.curOp.forceUpdate = true;
7169 adjustScrollWhenAboveVisible(cm, line, diff);
7170 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7171 });
7172 }
7173 };
7174 eventMixin(LineWidget);
7175
7176 function adjustScrollWhenAboveVisible(cm, line, diff) {
7177 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7178 { addToScrollTop(cm, diff); }
7179 }
7180
7181 function addLineWidget(doc, handle, node, options) {
7182 var widget = new LineWidget(doc, node, options);
7183 var cm = doc.cm;
7184 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7185 changeLine(doc, handle, "widget", function (line) {
7186 var widgets = line.widgets || (line.widgets = []);
7187 if (widget.insertAt == null) { widgets.push(widget); }
7188 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7189 widget.line = line;
7190 if (cm && !lineIsHidden(doc, line)) {
7191 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7192 updateLineHeight(line, line.height + widgetHeight(widget));
7193 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7194 cm.curOp.forceUpdate = true;
7195 }
7196 return true
7197 });
7198 signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle));
7199 return widget
7200 }
7201
7202 // TEXTMARKERS
7203
7204 // Created with markText and setBookmark methods. A TextMarker is a
7205 // handle that can be used to clear or find a marked position in the
7206 // document. Line objects hold arrays (markedSpans) containing
7207 // {from, to, marker} object pointing to such marker objects, and
7208 // indicating that such a marker is present on that line. Multiple
7209 // lines may point to the same marker when it spans across lines.
7210 // The spans will have null for their from/to properties when the
7211 // marker continues beyond the start/end of the line. Markers have
7212 // links back to the lines they currently touch.
7213
7214 // Collapsed markers have unique ids, in order to be able to order
7215 // them, which is needed for uniquely determining an outer marker
7216 // when they overlap (they may nest, but not partially overlap).
7217 var nextMarkerId = 0;
7218
7219 var TextMarker = function(doc, type) {
7220 this.lines = [];
7221 this.type = type;
7222 this.doc = doc;
7223 this.id = ++nextMarkerId;
7224 };
7225
7226 // Clear the marker.
7227 TextMarker.prototype.clear = function () {
7228 var this$1 = this;
7229
7230 if (this.explicitlyCleared) { return }
7231 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7232 if (withOp) { startOperation(cm); }
7233 if (hasHandler(this, "clear")) {
7234 var found = this.find();
7235 if (found) { signalLater(this, "clear", found.from, found.to); }
7236 }
7237 var min = null, max = null;
7238 for (var i = 0; i < this.lines.length; ++i) {
7239 var line = this$1.lines[i];
7240 var span = getMarkedSpanFor(line.markedSpans, this$1);
7241 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7242 else if (cm) {
7243 if (span.to != null) { max = lineNo(line); }
7244 if (span.from != null) { min = lineNo(line); }
7245 }
7246 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7247 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7248 { updateLineHeight(line, textHeight(cm.display)); }
7249 }
7250 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7251 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7252 if (len > cm.display.maxLineLength) {
7253 cm.display.maxLine = visual;
7254 cm.display.maxLineLength = len;
7255 cm.display.maxLineChanged = true;
7256 }
7257 } }
7258
7259 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7260 this.lines.length = 0;
7261 this.explicitlyCleared = true;
7262 if (this.atomic && this.doc.cantEdit) {
7263 this.doc.cantEdit = false;
7264 if (cm) { reCheckSelection(cm.doc); }
7265 }
7266 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7267 if (withOp) { endOperation(cm); }
7268 if (this.parent) { this.parent.clear(); }
7269 };
7270
7271 // Find the position of the marker in the document. Returns a {from,
7272 // to} object by default. Side can be passed to get a specific side
7273 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7274 // Pos objects returned contain a line object, rather than a line
7275 // number (used to prevent looking up the same line twice).
7276 TextMarker.prototype.find = function (side, lineObj) {
7277 var this$1 = this;
7278
7279 if (side == null && this.type == "bookmark") { side = 1; }
7280 var from, to;
7281 for (var i = 0; i < this.lines.length; ++i) {
7282 var line = this$1.lines[i];
7283 var span = getMarkedSpanFor(line.markedSpans, this$1);
7284 if (span.from != null) {
7285 from = Pos(lineObj ? line : lineNo(line), span.from);
7286 if (side == -1) { return from }
7287 }
7288 if (span.to != null) {
7289 to = Pos(lineObj ? line : lineNo(line), span.to);
7290 if (side == 1) { return to }
7291 }
7292 }
7293 return from && {from: from, to: to}
7294 };
7295
7296 // Signals that the marker's widget changed, and surrounding layout
7297 // should be recomputed.
7298 TextMarker.prototype.changed = function () {
7299 var this$1 = this;
7300
7301 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7302 if (!pos || !cm) { return }
7303 runInOp(cm, function () {
7304 var line = pos.line, lineN = lineNo(pos.line);
7305 var view = findViewForLine(cm, lineN);
7306 if (view) {
7307 clearLineMeasurementCacheFor(view);
7308 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7309 }
7310 cm.curOp.updateMaxLine = true;
7311 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7312 var oldHeight = widget.height;
7313 widget.height = null;
7314 var dHeight = widgetHeight(widget) - oldHeight;
7315 if (dHeight)
7316 { updateLineHeight(line, line.height + dHeight); }
7317 }
7318 signalLater(cm, "markerChanged", cm, this$1);
7319 });
7320 };
7321
7322 TextMarker.prototype.attachLine = function (line) {
7323 if (!this.lines.length && this.doc.cm) {
7324 var op = this.doc.cm.curOp;
7325 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7326 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7327 }
7328 this.lines.push(line);
7329 };
7330
7331 TextMarker.prototype.detachLine = function (line) {
7332 this.lines.splice(indexOf(this.lines, line), 1);
7333 if (!this.lines.length && this.doc.cm) {
7334 var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7335 }
7336 };
7337 eventMixin(TextMarker);
7338
7339 // Create a marker, wire it up to the right lines, and
7340 function markText(doc, from, to, options, type) {
7341 // Shared markers (across linked documents) are handled separately
7342 // (markTextShared will call out to this again, once per
7343 // document).
7344 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7345 // Ensure we are in an operation.
7346 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7347
7348 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7349 if (options) { copyObj(options, marker, false); }
7350 // Don't connect empty markers unless clearWhenEmpty is false
7351 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7352 { return marker }
7353 if (marker.replacedWith) {
7354 // Showing up as a widget implies collapsed (widget replaces text)
7355 marker.collapsed = true;
7356 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7357 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7358 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7359 }
7360 if (marker.collapsed) {
7361 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7362 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7363 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7364 seeCollapsedSpans();
7365 }
7366
7367 if (marker.addToHistory)
7368 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7369
7370 var curLine = from.line, cm = doc.cm, updateMaxLine;
7371 doc.iter(curLine, to.line + 1, function (line) {
7372 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7373 { updateMaxLine = true; }
7374 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7375 addMarkedSpan(line, new MarkedSpan(marker,
7376 curLine == from.line ? from.ch : null,
7377 curLine == to.line ? to.ch : null));
7378 ++curLine;
7379 });
7380 // lineIsHidden depends on the presence of the spans, so needs a second pass
7381 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7382 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7383 }); }
7384
7385 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7386
7387 if (marker.readOnly) {
7388 seeReadOnlySpans();
7389 if (doc.history.done.length || doc.history.undone.length)
7390 { doc.clearHistory(); }
7391 }
7392 if (marker.collapsed) {
7393 marker.id = ++nextMarkerId;
7394 marker.atomic = true;
7395 }
7396 if (cm) {
7397 // Sync editor state
7398 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7399 if (marker.collapsed)
7400 { regChange(cm, from.line, to.line + 1); }
7401 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
7402 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7403 if (marker.atomic) { reCheckSelection(cm.doc); }
7404 signalLater(cm, "markerAdded", cm, marker);
7405 }
7406 return marker
7407 }
7408
7409 // SHARED TEXTMARKERS
7410
7411 // A shared marker spans multiple linked documents. It is
7412 // implemented as a meta-marker-object controlling multiple normal
7413 // markers.
7414 var SharedTextMarker = function(markers, primary) {
7415 var this$1 = this;
7416
7417 this.markers = markers;
7418 this.primary = primary;
7419 for (var i = 0; i < markers.length; ++i)
7420 { markers[i].parent = this$1; }
7421 };
7422
7423 SharedTextMarker.prototype.clear = function () {
7424 var this$1 = this;
7425
7426 if (this.explicitlyCleared) { return }
7427 this.explicitlyCleared = true;
7428 for (var i = 0; i < this.markers.length; ++i)
7429 { this$1.markers[i].clear(); }
7430 signalLater(this, "clear");
7431 };
7432
7433 SharedTextMarker.prototype.find = function (side, lineObj) {
7434 return this.primary.find(side, lineObj)
7435 };
7436 eventMixin(SharedTextMarker);
7437
7438 function markTextShared(doc, from, to, options, type) {
7439 options = copyObj(options);
7440 options.shared = false;
7441 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7442 var widget = options.widgetNode;
7443 linkedDocs(doc, function (doc) {
7444 if (widget) { options.widgetNode = widget.cloneNode(true); }
7445 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7446 for (var i = 0; i < doc.linked.length; ++i)
7447 { if (doc.linked[i].isParent) { return } }
7448 primary = lst(markers);
7449 });
7450 return new SharedTextMarker(markers, primary)
7451 }
7452
7453 function findSharedMarkers(doc) {
7454 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7455 }
7456
7457 function copySharedMarkers(doc, markers) {
7458 for (var i = 0; i < markers.length; i++) {
7459 var marker = markers[i], pos = marker.find();
7460 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7461 if (cmp(mFrom, mTo)) {
7462 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7463 marker.markers.push(subMark);
7464 subMark.parent = marker;
7465 }
7466 }
7467 }
7468
7469 function detachSharedMarkers(markers) {
7470 var loop = function ( i ) {
7471 var marker = markers[i], linked = [marker.primary.doc];
7472 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7473 for (var j = 0; j < marker.markers.length; j++) {
7474 var subMarker = marker.markers[j];
7475 if (indexOf(linked, subMarker.doc) == -1) {
7476 subMarker.parent = null;
7477 marker.markers.splice(j--, 1);
7478 }
7479 }
7480 };
7481
7482 for (var i = 0; i < markers.length; i++) loop( i );
7483 }
7484
7485 var nextDocId = 0;
7486 var Doc = function(text, mode, firstLine, lineSep, direction) {
7487 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7488 if (firstLine == null) { firstLine = 0; }
7489
7490 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7491 this.first = firstLine;
7492 this.scrollTop = this.scrollLeft = 0;
7493 this.cantEdit = false;
7494 this.cleanGeneration = 1;
7495 this.modeFrontier = this.highlightFrontier = firstLine;
7496 var start = Pos(firstLine, 0);
7497 this.sel = simpleSelection(start);
7498 this.history = new History(null);
7499 this.id = ++nextDocId;
7500 this.modeOption = mode;
7501 this.lineSep = lineSep;
7502 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7503 this.extend = false;
7504
7505 if (typeof text == "string") { text = this.splitLines(text); }
7506 updateDoc(this, {from: start, to: start, text: text});
7507 setSelection(this, simpleSelection(start), sel_dontScroll);
7508 };
7509
7510 Doc.prototype = createObj(BranchChunk.prototype, {
7511 constructor: Doc,
7512 // Iterate over the document. Supports two forms -- with only one
7513 // argument, it calls that for each line in the document. With
7514 // three, it iterates over the range given by the first two (with
7515 // the second being non-inclusive).
7516 iter: function(from, to, op) {
7517 if (op) { this.iterN(from - this.first, to - from, op); }
7518 else { this.iterN(this.first, this.first + this.size, from); }
7519 },
7520
7521 // Non-public interface for adding and removing lines.
7522 insert: function(at, lines) {
7523 var height = 0;
7524 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7525 this.insertInner(at - this.first, lines, height);
7526 },
7527 remove: function(at, n) { this.removeInner(at - this.first, n); },
7528
7529 // From here, the methods are part of the public interface. Most
7530 // are also available from CodeMirror (editor) instances.
7531
7532 getValue: function(lineSep) {
7533 var lines = getLines(this, this.first, this.first + this.size);
7534 if (lineSep === false) { return lines }
7535 return lines.join(lineSep || this.lineSeparator())
7536 },
7537 setValue: docMethodOp(function(code) {
7538 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7539 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7540 text: this.splitLines(code), origin: "setValue", full: true}, true);
7541 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7542 setSelection(this, simpleSelection(top), sel_dontScroll);
7543 }),
7544 replaceRange: function(code, from, to, origin) {
7545 from = clipPos(this, from);
7546 to = to ? clipPos(this, to) : from;
7547 replaceRange(this, code, from, to, origin);
7548 },
7549 getRange: function(from, to, lineSep) {
7550 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7551 if (lineSep === false) { return lines }
7552 return lines.join(lineSep || this.lineSeparator())
7553 },
7554
7555 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7556
7557 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7558 getLineNumber: function(line) {return lineNo(line)},
7559
7560 getLineHandleVisualStart: function(line) {
7561 if (typeof line == "number") { line = getLine(this, line); }
7562 return visualLine(line)
7563 },
7564
7565 lineCount: function() {return this.size},
7566 firstLine: function() {return this.first},
7567 lastLine: function() {return this.first + this.size - 1},
7568
7569 clipPos: function(pos) {return clipPos(this, pos)},
7570
7571 getCursor: function(start) {
7572 var range$$1 = this.sel.primary(), pos;
7573 if (start == null || start == "head") { pos = range$$1.head; }
7574 else if (start == "anchor") { pos = range$$1.anchor; }
7575 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7576 else { pos = range$$1.from(); }
7577 return pos
7578 },
7579 listSelections: function() { return this.sel.ranges },
7580 somethingSelected: function() {return this.sel.somethingSelected()},
7581
7582 setCursor: docMethodOp(function(line, ch, options) {
7583 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7584 }),
7585 setSelection: docMethodOp(function(anchor, head, options) {
7586 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7587 }),
7588 extendSelection: docMethodOp(function(head, other, options) {
7589 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7590 }),
7591 extendSelections: docMethodOp(function(heads, options) {
7592 extendSelections(this, clipPosArray(this, heads), options);
7593 }),
7594 extendSelectionsBy: docMethodOp(function(f, options) {
7595 var heads = map(this.sel.ranges, f);
7596 extendSelections(this, clipPosArray(this, heads), options);
7597 }),
7598 setSelections: docMethodOp(function(ranges, primary, options) {
7599 var this$1 = this;
7600
7601 if (!ranges.length) { return }
7602 var out = [];
7603 for (var i = 0; i < ranges.length; i++)
7604 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7605 clipPos(this$1, ranges[i].head)); }
7606 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7607 setSelection(this, normalizeSelection(out, primary), options);
7608 }),
7609 addSelection: docMethodOp(function(anchor, head, options) {
7610 var ranges = this.sel.ranges.slice(0);
7611 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7612 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7613 }),
7614
7615 getSelection: function(lineSep) {
7616 var this$1 = this;
7617
7618 var ranges = this.sel.ranges, lines;
7619 for (var i = 0; i < ranges.length; i++) {
7620 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7621 lines = lines ? lines.concat(sel) : sel;
7622 }
7623 if (lineSep === false) { return lines }
7624 else { return lines.join(lineSep || this.lineSeparator()) }
7625 },
7626 getSelections: function(lineSep) {
7627 var this$1 = this;
7628
7629 var parts = [], ranges = this.sel.ranges;
7630 for (var i = 0; i < ranges.length; i++) {
7631 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7632 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7633 parts[i] = sel;
7634 }
7635 return parts
7636 },
7637 replaceSelection: function(code, collapse, origin) {
7638 var dup = [];
7639 for (var i = 0; i < this.sel.ranges.length; i++)
7640 { dup[i] = code; }
7641 this.replaceSelections(dup, collapse, origin || "+input");
7642 },
7643 replaceSelections: docMethodOp(function(code, collapse, origin) {
7644 var this$1 = this;
7645
7646 var changes = [], sel = this.sel;
7647 for (var i = 0; i < sel.ranges.length; i++) {
7648 var range$$1 = sel.ranges[i];
7649 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7650 }
7651 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7652 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7653 { makeChange(this$1, changes[i$1]); }
7654 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7655 else if (this.cm) { ensureCursorVisible(this.cm); }
7656 }),
7657 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7658 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7659 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7660 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7661
7662 setExtending: function(val) {this.extend = val;},
7663 getExtending: function() {return this.extend},
7664
7665 historySize: function() {
7666 var hist = this.history, done = 0, undone = 0;
7667 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7668 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7669 return {undo: done, redo: undone}
7670 },
7671 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7672
7673 markClean: function() {
7674 this.cleanGeneration = this.changeGeneration(true);
7675 },
7676 changeGeneration: function(forceSplit) {
7677 if (forceSplit)
7678 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7679 return this.history.generation
7680 },
7681 isClean: function (gen) {
7682 return this.history.generation == (gen || this.cleanGeneration)
7683 },
7684
7685 getHistory: function() {
7686 return {done: copyHistoryArray(this.history.done),
7687 undone: copyHistoryArray(this.history.undone)}
7688 },
7689 setHistory: function(histData) {
7690 var hist = this.history = new History(this.history.maxGeneration);
7691 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7692 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7693 },
7694
7695 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7696 return changeLine(this, line, "gutter", function (line) {
7697 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7698 markers[gutterID] = value;
7699 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7700 return true
7701 })
7702 }),
7703
7704 clearGutter: docMethodOp(function(gutterID) {
7705 var this$1 = this;
7706
7707 this.iter(function (line) {
7708 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7709 changeLine(this$1, line, "gutter", function () {
7710 line.gutterMarkers[gutterID] = null;
7711 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7712 return true
7713 });
7714 }
7715 });
7716 }),
7717
7718 lineInfo: function(line) {
7719 var n;
7720 if (typeof line == "number") {
7721 if (!isLine(this, line)) { return null }
7722 n = line;
7723 line = getLine(this, line);
7724 if (!line) { return null }
7725 } else {
7726 n = lineNo(line);
7727 if (n == null) { return null }
7728 }
7729 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7730 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7731 widgets: line.widgets}
7732 },
7733
7734 addLineClass: docMethodOp(function(handle, where, cls) {
7735 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7736 var prop = where == "text" ? "textClass"
7737 : where == "background" ? "bgClass"
7738 : where == "gutter" ? "gutterClass" : "wrapClass";
7739 if (!line[prop]) { line[prop] = cls; }
7740 else if (classTest(cls).test(line[prop])) { return false }
7741 else { line[prop] += " " + cls; }
7742 return true
7743 })
7744 }),
7745 removeLineClass: docMethodOp(function(handle, where, cls) {
7746 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7747 var prop = where == "text" ? "textClass"
7748 : where == "background" ? "bgClass"
7749 : where == "gutter" ? "gutterClass" : "wrapClass";
7750 var cur = line[prop];
7751 if (!cur) { return false }
7752 else if (cls == null) { line[prop] = null; }
7753 else {
7754 var found = cur.match(classTest(cls));
7755 if (!found) { return false }
7756 var end = found.index + found[0].length;
7757 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7758 }
7759 return true
7760 })
7761 }),
7762
7763 addLineWidget: docMethodOp(function(handle, node, options) {
7764 return addLineWidget(this, handle, node, options)
7765 }),
7766 removeLineWidget: function(widget) { widget.clear(); },
7767
7768 markText: function(from, to, options) {
7769 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7770 },
7771 setBookmark: function(pos, options) {
7772 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7773 insertLeft: options && options.insertLeft,
7774 clearWhenEmpty: false, shared: options && options.shared,
7775 handleMouseEvents: options && options.handleMouseEvents};
7776 pos = clipPos(this, pos);
7777 return markText(this, pos, pos, realOpts, "bookmark")
7778 },
7779 findMarksAt: function(pos) {
7780 pos = clipPos(this, pos);
7781 var markers = [], spans = getLine(this, pos.line).markedSpans;
7782 if (spans) { for (var i = 0; i < spans.length; ++i) {
7783 var span = spans[i];
7784 if ((span.from == null || span.from <= pos.ch) &&
7785 (span.to == null || span.to >= pos.ch))
7786 { markers.push(span.marker.parent || span.marker); }
7787 } }
7788 return markers
7789 },
7790 findMarks: function(from, to, filter) {
7791 from = clipPos(this, from); to = clipPos(this, to);
7792 var found = [], lineNo$$1 = from.line;
7793 this.iter(from.line, to.line + 1, function (line) {
7794 var spans = line.markedSpans;
7795 if (spans) { for (var i = 0; i < spans.length; i++) {
7796 var span = spans[i];
7797 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7798 span.from == null && lineNo$$1 != from.line ||
7799 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7800 (!filter || filter(span.marker)))
7801 { found.push(span.marker.parent || span.marker); }
7802 } }
7803 ++lineNo$$1;
7804 });
7805 return found
7806 },
7807 getAllMarks: function() {
7808 var markers = [];
7809 this.iter(function (line) {
7810 var sps = line.markedSpans;
7811 if (sps) { for (var i = 0; i < sps.length; ++i)
7812 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7813 });
7814 return markers
7815 },
7816
7817 posFromIndex: function(off) {
7818 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7819 this.iter(function (line) {
7820 var sz = line.text.length + sepSize;
7821 if (sz > off) { ch = off; return true }
7822 off -= sz;
7823 ++lineNo$$1;
7824 });
7825 return clipPos(this, Pos(lineNo$$1, ch))
7826 },
7827 indexFromPos: function (coords) {
7828 coords = clipPos(this, coords);
7829 var index = coords.ch;
7830 if (coords.line < this.first || coords.ch < 0) { return 0 }
7831 var sepSize = this.lineSeparator().length;
7832 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
7833 index += line.text.length + sepSize;
7834 });
7835 return index
7836 },
7837
7838 copy: function(copyHistory) {
7839 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7840 this.modeOption, this.first, this.lineSep, this.direction);
7841 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7842 doc.sel = this.sel;
7843 doc.extend = false;
7844 if (copyHistory) {
7845 doc.history.undoDepth = this.history.undoDepth;
7846 doc.setHistory(this.getHistory());
7847 }
7848 return doc
7849 },
7850
7851 linkedDoc: function(options) {
7852 if (!options) { options = {}; }
7853 var from = this.first, to = this.first + this.size;
7854 if (options.from != null && options.from > from) { from = options.from; }
7855 if (options.to != null && options.to < to) { to = options.to; }
7856 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
7857 if (options.sharedHist) { copy.history = this.history
7858 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7859 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7860 copySharedMarkers(copy, findSharedMarkers(this));
7861 return copy
7862 },
7863 unlinkDoc: function(other) {
7864 var this$1 = this;
7865
7866 if (other instanceof CodeMirror$1) { other = other.doc; }
7867 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
7868 var link = this$1.linked[i];
7869 if (link.doc != other) { continue }
7870 this$1.linked.splice(i, 1);
7871 other.unlinkDoc(this$1);
7872 detachSharedMarkers(findSharedMarkers(this$1));
7873 break
7874 } }
7875 // If the histories were shared, split them again
7876 if (other.history == this.history) {
7877 var splitIds = [other.id];
7878 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
7879 other.history = new History(null);
7880 other.history.done = copyHistoryArray(this.history.done, splitIds);
7881 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7882 }
7883 },
7884 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7885
7886 getMode: function() {return this.mode},
7887 getEditor: function() {return this.cm},
7888
7889 splitLines: function(str) {
7890 if (this.lineSep) { return str.split(this.lineSep) }
7891 return splitLinesAuto(str)
7892 },
7893 lineSeparator: function() { return this.lineSep || "\n" },
7894
7895 setDirection: docMethodOp(function (dir) {
7896 if (dir != "rtl") { dir = "ltr"; }
7897 if (dir == this.direction) { return }
7898 this.direction = dir;
7899 this.iter(function (line) { return line.order = null; });
7900 if (this.cm) { directionChanged(this.cm); }
7901 })
7902 });
7903
7904 // Public alias.
7905 Doc.prototype.eachLine = Doc.prototype.iter;
7906
7907 // Kludge to work around strange IE behavior where it'll sometimes
7908 // re-fire a series of drag-related events right after the drop (#1551)
7909 var lastDrop = 0;
7910
7911 function onDrop(e) {
7912 var cm = this;
7913 clearDragCursor(cm);
7914 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
7915 { return }
7916 e_preventDefault(e);
7917 if (ie) { lastDrop = +new Date; }
7918 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
7919 if (!pos || cm.isReadOnly()) { return }
7920 // Might be a file drop, in which case we simply extract the text
7921 // and insert it.
7922 if (files && files.length && window.FileReader && window.File) {
7923 var n = files.length, text = Array(n), read = 0;
7924 var loadFile = function (file, i) {
7925 if (cm.options.allowDropFileTypes &&
7926 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
7927 { return }
7928
7929 var reader = new FileReader;
7930 reader.onload = operation(cm, function () {
7931 var content = reader.result;
7932 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
7933 text[i] = content;
7934 if (++read == n) {
7935 pos = clipPos(cm.doc, pos);
7936 var change = {from: pos, to: pos,
7937 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
7938 origin: "paste"};
7939 makeChange(cm.doc, change);
7940 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
7941 }
7942 });
7943 reader.readAsText(file);
7944 };
7945 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
7946 } else { // Normal drop
7947 // Don't do a replace if the drop happened inside of the selected text.
7948 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
7949 cm.state.draggingText(e);
7950 // Ensure the editor is re-focused
7951 setTimeout(function () { return cm.display.input.focus(); }, 20);
7952 return
7953 }
7954 try {
7955 var text$1 = e.dataTransfer.getData("Text");
7956 if (text$1) {
7957 var selected;
7958 if (cm.state.draggingText && !cm.state.draggingText.copy)
7959 { selected = cm.listSelections(); }
7960 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
7961 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
7962 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
7963 cm.replaceSelection(text$1, "around", "paste");
7964 cm.display.input.focus();
7965 }
7966 }
7967 catch(e){}
7968 }
7969 }
7970
7971 function onDragStart(cm, e) {
7972 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
7973 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
7974
7975 e.dataTransfer.setData("Text", cm.getSelection());
7976 e.dataTransfer.effectAllowed = "copyMove";
7977
7978 // Use dummy image instead of default browsers image.
7979 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
7980 if (e.dataTransfer.setDragImage && !safari) {
7981 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
7982 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
7983 if (presto) {
7984 img.width = img.height = 1;
7985 cm.display.wrapper.appendChild(img);
7986 // Force a relayout, or Opera won't use our image for some obscure reason
7987 img._top = img.offsetTop;
7988 }
7989 e.dataTransfer.setDragImage(img, 0, 0);
7990 if (presto) { img.parentNode.removeChild(img); }
7991 }
7992 }
7993
7994 function onDragOver(cm, e) {
7995 var pos = posFromMouse(cm, e);
7996 if (!pos) { return }
7997 var frag = document.createDocumentFragment();
7998 drawSelectionCursor(cm, pos, frag);
7999 if (!cm.display.dragCursor) {
8000 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8001 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8002 }
8003 removeChildrenAndAdd(cm.display.dragCursor, frag);
8004 }
8005
8006 function clearDragCursor(cm) {
8007 if (cm.display.dragCursor) {
8008 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8009 cm.display.dragCursor = null;
8010 }
8011 }
8012
8013 // These must be handled carefully, because naively registering a
8014 // handler for each editor will cause the editors to never be
8015 // garbage collected.
8016
8017 function forEachCodeMirror(f) {
8018 if (!document.getElementsByClassName) { return }
8019 var byClass = document.getElementsByClassName("CodeMirror");
8020 for (var i = 0; i < byClass.length; i++) {
8021 var cm = byClass[i].CodeMirror;
8022 if (cm) { f(cm); }
8023 }
8024 }
8025
8026 var globalsRegistered = false;
8027 function ensureGlobalHandlers() {
8028 if (globalsRegistered) { return }
8029 registerGlobalHandlers();
8030 globalsRegistered = true;
8031 }
8032 function registerGlobalHandlers() {
8033 // When the window resizes, we need to refresh active editors.
8034 var resizeTimer;
8035 on(window, "resize", function () {
8036 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8037 resizeTimer = null;
8038 forEachCodeMirror(onResize);
8039 }, 100); }
8040 });
8041 // When the window loses focus, we want to show the editor as blurred
8042 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8043 }
8044 // Called when the window resizes
8045 function onResize(cm) {
8046 var d = cm.display;
8047 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
8048 { return }
8049 // Might be a text scaling operation, clear size caches.
8050 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8051 d.scrollbarsClipped = false;
8052 cm.setSize();
8053 }
8054
8055 var keyNames = {
8056 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8057 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8058 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8059 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8060 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
8061 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8062 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8063 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8064 };
8065
8066 // Number keys
8067 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8068 // Alphabetic keys
8069 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8070 // Function keys
8071 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8072
8073 var keyMap = {};
8074
8075 keyMap.basic = {
8076 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8077 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8078 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8079 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8080 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8081 "Esc": "singleSelection"
8082 };
8083 // Note that the save and find-related commands aren't defined by
8084 // default. User code or addons can define them. Unknown commands
8085 // are simply ignored.
8086 keyMap.pcDefault = {
8087 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8088 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8089 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8090 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8091 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8092 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8093 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8094 fallthrough: "basic"
8095 };
8096 // Very basic readline/emacs-style bindings, which are standard on Mac.
8097 keyMap.emacsy = {
8098 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8099 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8100 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8101 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8102 "Ctrl-O": "openLine"
8103 };
8104 keyMap.macDefault = {
8105 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8106 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8107 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8108 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8109 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8110 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8111 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8112 fallthrough: ["basic", "emacsy"]
8113 };
8114 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8115
8116 // KEYMAP DISPATCH
8117
8118 function normalizeKeyName(name) {
8119 var parts = name.split(/-(?!$)/);
8120 name = parts[parts.length - 1];
8121 var alt, ctrl, shift, cmd;
8122 for (var i = 0; i < parts.length - 1; i++) {
8123 var mod = parts[i];
8124 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8125 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8126 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8127 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8128 else { throw new Error("Unrecognized modifier name: " + mod) }
8129 }
8130 if (alt) { name = "Alt-" + name; }
8131 if (ctrl) { name = "Ctrl-" + name; }
8132 if (cmd) { name = "Cmd-" + name; }
8133 if (shift) { name = "Shift-" + name; }
8134 return name
8135 }
8136
8137 // This is a kludge to keep keymaps mostly working as raw objects
8138 // (backwards compatibility) while at the same time support features
8139 // like normalization and multi-stroke key bindings. It compiles a
8140 // new normalized keymap, and then updates the old object to reflect
8141 // this.
8142 function normalizeKeyMap(keymap) {
8143 var copy = {};
8144 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8145 var value = keymap[keyname];
8146 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8147 if (value == "...") { delete keymap[keyname]; continue }
8148
8149 var keys = map(keyname.split(" "), normalizeKeyName);
8150 for (var i = 0; i < keys.length; i++) {
8151 var val = (void 0), name = (void 0);
8152 if (i == keys.length - 1) {
8153 name = keys.join(" ");
8154 val = value;
8155 } else {
8156 name = keys.slice(0, i + 1).join(" ");
8157 val = "...";
8158 }
8159 var prev = copy[name];
8160 if (!prev) { copy[name] = val; }
8161 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8162 }
8163 delete keymap[keyname];
8164 } }
8165 for (var prop in copy) { keymap[prop] = copy[prop]; }
8166 return keymap
8167 }
8168
8169 function lookupKey(key, map$$1, handle, context) {
8170 map$$1 = getKeyMap(map$$1);
8171 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8172 if (found === false) { return "nothing" }
8173 if (found === "...") { return "multi" }
8174 if (found != null && handle(found)) { return "handled" }
8175
8176 if (map$$1.fallthrough) {
8177 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8178 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8179 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8180 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8181 if (result) { return result }
8182 }
8183 }
8184 }
8185
8186 // Modifier key presses don't count as 'real' key presses for the
8187 // purpose of keymap fallthrough.
8188 function isModifierKey(value) {
8189 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8190 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8191 }
8192
8193 function addModifierNames(name, event, noShift) {
8194 var base = name;
8195 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8196 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8197 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8198 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8199 return name
8200 }
8201
8202 // Look up the name of a key as indicated by an event object.
8203 function keyName(event, noShift) {
8204 if (presto && event.keyCode == 34 && event["char"]) { return false }
8205 var name = keyNames[event.keyCode];
8206 if (name == null || event.altGraphKey) { return false }
8207 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8208 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8209 if (event.keyCode == 3 && event.code) { name = event.code; }
8210 return addModifierNames(name, event, noShift)
8211 }
8212
8213 function getKeyMap(val) {
8214 return typeof val == "string" ? keyMap[val] : val
8215 }
8216
8217 // Helper for deleting text near the selection(s), used to implement
8218 // backspace, delete, and similar functionality.
8219 function deleteNearSelection(cm, compute) {
8220 var ranges = cm.doc.sel.ranges, kill = [];
8221 // Build up a set of ranges to kill first, merging overlapping
8222 // ranges.
8223 for (var i = 0; i < ranges.length; i++) {
8224 var toKill = compute(ranges[i]);
8225 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8226 var replaced = kill.pop();
8227 if (cmp(replaced.from, toKill.from) < 0) {
8228 toKill.from = replaced.from;
8229 break
8230 }
8231 }
8232 kill.push(toKill);
8233 }
8234 // Next, remove those actual ranges.
8235 runInOp(cm, function () {
8236 for (var i = kill.length - 1; i >= 0; i--)
8237 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8238 ensureCursorVisible(cm);
8239 });
8240 }
8241
8242 function moveCharLogically(line, ch, dir) {
8243 var target = skipExtendingChars(line.text, ch + dir, dir);
8244 return target < 0 || target > line.text.length ? null : target
8245 }
8246
8247 function moveLogically(line, start, dir) {
8248 var ch = moveCharLogically(line, start.ch, dir);
8249 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8250 }
8251
8252 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8253 if (visually) {
8254 var order = getOrder(lineObj, cm.doc.direction);
8255 if (order) {
8256 var part = dir < 0 ? lst(order) : order[0];
8257 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8258 var sticky = moveInStorageOrder ? "after" : "before";
8259 var ch;
8260 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8261 // it could be that the last bidi part is not on the last visual line,
8262 // since visual lines contain content order-consecutive chunks.
8263 // Thus, in rtl, we are looking for the first (content-order) character
8264 // in the rtl chunk that is on the last line (that is, the same line
8265 // as the last (content-order) character).
8266 if (part.level > 0 || cm.doc.direction == "rtl") {
8267 var prep = prepareMeasureForLine(cm, lineObj);
8268 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8269 var targetTop = measureCharPrepared(cm, prep, ch).top;
8270 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8271 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8272 } else { ch = dir < 0 ? part.to : part.from; }
8273 return new Pos(lineNo, ch, sticky)
8274 }
8275 }
8276 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8277 }
8278
8279 function moveVisually(cm, line, start, dir) {
8280 var bidi = getOrder(line, cm.doc.direction);
8281 if (!bidi) { return moveLogically(line, start, dir) }
8282 if (start.ch >= line.text.length) {
8283 start.ch = line.text.length;
8284 start.sticky = "before";
8285 } else if (start.ch <= 0) {
8286 start.ch = 0;
8287 start.sticky = "after";
8288 }
8289 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8290 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8291 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8292 // nothing interesting happens.
8293 return moveLogically(line, start, dir)
8294 }
8295
8296 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8297 var prep;
8298 var getWrappedLineExtent = function (ch) {
8299 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8300 prep = prep || prepareMeasureForLine(cm, line);
8301 return wrappedLineExtentChar(cm, line, prep, ch)
8302 };
8303 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8304
8305 if (cm.doc.direction == "rtl" || part.level == 1) {
8306 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8307 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8308 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8309 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8310 var sticky = moveInStorageOrder ? "before" : "after";
8311 return new Pos(start.line, ch, sticky)
8312 }
8313 }
8314
8315 // Case 3: Could not move within this bidi part in this visual line, so leave
8316 // the current bidi part
8317
8318 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8319 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8320 ? new Pos(start.line, mv(ch, 1), "before")
8321 : new Pos(start.line, ch, "after"); };
8322
8323 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8324 var part = bidi[partPos];
8325 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8326 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8327 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8328 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8329 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8330 }
8331 };
8332
8333 // Case 3a: Look for other bidi parts on the same visual line
8334 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8335 if (res) { return res }
8336
8337 // Case 3b: Look for other bidi parts on the next visual line
8338 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8339 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8340 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8341 if (res) { return res }
8342 }
8343
8344 // Case 4: Nowhere to move
8345 return null
8346 }
8347
8348 // Commands are parameter-less actions that can be performed on an
8349 // editor, mostly used for keybindings.
8350 var commands = {
8351 selectAll: selectAll,
8352 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8353 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8354 if (range.empty()) {
8355 var len = getLine(cm.doc, range.head.line).text.length;
8356 if (range.head.ch == len && range.head.line < cm.lastLine())
8357 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8358 else
8359 { return {from: range.head, to: Pos(range.head.line, len)} }
8360 } else {
8361 return {from: range.from(), to: range.to()}
8362 }
8363 }); },
8364 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8365 from: Pos(range.from().line, 0),
8366 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8367 }); }); },
8368 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8369 from: Pos(range.from().line, 0), to: range.from()
8370 }); }); },
8371 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8372 var top = cm.charCoords(range.head, "div").top + 5;
8373 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8374 return {from: leftPos, to: range.from()}
8375 }); },
8376 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8377 var top = cm.charCoords(range.head, "div").top + 5;
8378 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8379 return {from: range.from(), to: rightPos }
8380 }); },
8381 undo: function (cm) { return cm.undo(); },
8382 redo: function (cm) { return cm.redo(); },
8383 undoSelection: function (cm) { return cm.undoSelection(); },
8384 redoSelection: function (cm) { return cm.redoSelection(); },
8385 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8386 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8387 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8388 {origin: "+move", bias: 1}
8389 ); },
8390 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8391 {origin: "+move", bias: 1}
8392 ); },
8393 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8394 {origin: "+move", bias: -1}
8395 ); },
8396 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8397 var top = cm.cursorCoords(range.head, "div").top + 5;
8398 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8399 }, sel_move); },
8400 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8401 var top = cm.cursorCoords(range.head, "div").top + 5;
8402 return cm.coordsChar({left: 0, top: top}, "div")
8403 }, sel_move); },
8404 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8405 var top = cm.cursorCoords(range.head, "div").top + 5;
8406 var pos = cm.coordsChar({left: 0, top: top}, "div");
8407 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8408 return pos
8409 }, sel_move); },
8410 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8411 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8412 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8413 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8414 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8415 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8416 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8417 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8418 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8419 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8420 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8421 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8422 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8423 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8424 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8425 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8426 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8427 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8428 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8429 indentMore: function (cm) { return cm.indentSelection("add"); },
8430 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8431 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8432 insertSoftTab: function (cm) {
8433 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8434 for (var i = 0; i < ranges.length; i++) {
8435 var pos = ranges[i].from();
8436 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8437 spaces.push(spaceStr(tabSize - col % tabSize));
8438 }
8439 cm.replaceSelections(spaces);
8440 },
8441 defaultTab: function (cm) {
8442 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8443 else { cm.execCommand("insertTab"); }
8444 },
8445 // Swap the two chars left and right of each selection's head.
8446 // Move cursor behind the two swapped characters afterwards.
8447 //
8448 // Doesn't consider line feeds a character.
8449 // Doesn't scan more than one line above to find a character.
8450 // Doesn't do anything on an empty line.
8451 // Doesn't do anything with non-empty selections.
8452 transposeChars: function (cm) { return runInOp(cm, function () {
8453 var ranges = cm.listSelections(), newSel = [];
8454 for (var i = 0; i < ranges.length; i++) {
8455 if (!ranges[i].empty()) { continue }
8456 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8457 if (line) {
8458 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8459 if (cur.ch > 0) {
8460 cur = new Pos(cur.line, cur.ch + 1);
8461 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8462 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8463 } else if (cur.line > cm.doc.first) {
8464 var prev = getLine(cm.doc, cur.line - 1).text;
8465 if (prev) {
8466 cur = new Pos(cur.line, 1);
8467 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8468 prev.charAt(prev.length - 1),
8469 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8470 }
8471 }
8472 }
8473 newSel.push(new Range(cur, cur));
8474 }
8475 cm.setSelections(newSel);
8476 }); },
8477 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8478 var sels = cm.listSelections();
8479 for (var i = sels.length - 1; i >= 0; i--)
8480 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8481 sels = cm.listSelections();
8482 for (var i$1 = 0; i$1 < sels.length; i$1++)
8483 { cm.indentLine(sels[i$1].from().line, null, true); }
8484 ensureCursorVisible(cm);
8485 }); },
8486 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8487 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8488 };
8489
8490
8491 function lineStart(cm, lineN) {
8492 var line = getLine(cm.doc, lineN);
8493 var visual = visualLine(line);
8494 if (visual != line) { lineN = lineNo(visual); }
8495 return endOfLine(true, cm, visual, lineN, 1)
8496 }
8497 function lineEnd(cm, lineN) {
8498 var line = getLine(cm.doc, lineN);
8499 var visual = visualLineEnd(line);
8500 if (visual != line) { lineN = lineNo(visual); }
8501 return endOfLine(true, cm, line, lineN, -1)
8502 }
8503 function lineStartSmart(cm, pos) {
8504 var start = lineStart(cm, pos.line);
8505 var line = getLine(cm.doc, start.line);
8506 var order = getOrder(line, cm.doc.direction);
8507 if (!order || order[0].level == 0) {
8508 var firstNonWS = Math.max(0, line.text.search(/\S/));
8509 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8510 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8511 }
8512 return start
8513 }
8514
8515 // Run a handler that was bound to a key.
8516 function doHandleBinding(cm, bound, dropShift) {
8517 if (typeof bound == "string") {
8518 bound = commands[bound];
8519 if (!bound) { return false }
8520 }
8521 // Ensure previous input has been read, so that the handler sees a
8522 // consistent view of the document
8523 cm.display.input.ensurePolled();
8524 var prevShift = cm.display.shift, done = false;
8525 try {
8526 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8527 if (dropShift) { cm.display.shift = false; }
8528 done = bound(cm) != Pass;
8529 } finally {
8530 cm.display.shift = prevShift;
8531 cm.state.suppressEdits = false;
8532 }
8533 return done
8534 }
8535
8536 function lookupKeyForEditor(cm, name, handle) {
8537 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8538 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8539 if (result) { return result }
8540 }
8541 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8542 || lookupKey(name, cm.options.keyMap, handle, cm)
8543 }
8544
8545 // Note that, despite the name, this function is also used to check
8546 // for bound mouse clicks.
8547
8548 var stopSeq = new Delayed;
8549
8550 function dispatchKey(cm, name, e, handle) {
8551 var seq = cm.state.keySeq;
8552 if (seq) {
8553 if (isModifierKey(name)) { return "handled" }
8554 if (/\'$/.test(name))
8555 { cm.state.keySeq = null; }
8556 else
8557 { stopSeq.set(50, function () {
8558 if (cm.state.keySeq == seq) {
8559 cm.state.keySeq = null;
8560 cm.display.input.reset();
8561 }
8562 }); }
8563 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8564 }
8565 return dispatchKeyInner(cm, name, e, handle)
8566 }
8567
8568 function dispatchKeyInner(cm, name, e, handle) {
8569 var result = lookupKeyForEditor(cm, name, handle);
8570
8571 if (result == "multi")
8572 { cm.state.keySeq = name; }
8573 if (result == "handled")
8574 { signalLater(cm, "keyHandled", cm, name, e); }
8575
8576 if (result == "handled" || result == "multi") {
8577 e_preventDefault(e);
8578 restartBlink(cm);
8579 }
8580
8581 return !!result
8582 }
8583
8584 // Handle a key from the keydown event.
8585 function handleKeyBinding(cm, e) {
8586 var name = keyName(e, true);
8587 if (!name) { return false }
8588
8589 if (e.shiftKey && !cm.state.keySeq) {
8590 // First try to resolve full name (including 'Shift-'). Failing
8591 // that, see if there is a cursor-motion command (starting with
8592 // 'go') bound to the keyname without 'Shift-'.
8593 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8594 || dispatchKey(cm, name, e, function (b) {
8595 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8596 { return doHandleBinding(cm, b) }
8597 })
8598 } else {
8599 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8600 }
8601 }
8602
8603 // Handle a key from the keypress event
8604 function handleCharBinding(cm, e, ch) {
8605 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8606 }
8607
8608 var lastStoppedKey = null;
8609 function onKeyDown(e) {
8610 var cm = this;
8611 cm.curOp.focus = activeElt();
8612 if (signalDOMEvent(cm, e)) { return }
8613 // IE does strange things with escape.
8614 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8615 var code = e.keyCode;
8616 cm.display.shift = code == 16 || e.shiftKey;
8617 var handled = handleKeyBinding(cm, e);
8618 if (presto) {
8619 lastStoppedKey = handled ? code : null;
8620 // Opera has no cut event... we try to at least catch the key combo
8621 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8622 { cm.replaceSelection("", null, "cut"); }
8623 }
8624
8625 // Turn mouse into crosshair when Alt is held on Mac.
8626 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8627 { showCrossHair(cm); }
8628 }
8629
8630 function showCrossHair(cm) {
8631 var lineDiv = cm.display.lineDiv;
8632 addClass(lineDiv, "CodeMirror-crosshair");
8633
8634 function up(e) {
8635 if (e.keyCode == 18 || !e.altKey) {
8636 rmClass(lineDiv, "CodeMirror-crosshair");
8637 off(document, "keyup", up);
8638 off(document, "mouseover", up);
8639 }
8640 }
8641 on(document, "keyup", up);
8642 on(document, "mouseover", up);
8643 }
8644
8645 function onKeyUp(e) {
8646 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8647 signalDOMEvent(this, e);
8648 }
8649
8650 function onKeyPress(e) {
8651 var cm = this;
8652 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8653 var keyCode = e.keyCode, charCode = e.charCode;
8654 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8655 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8656 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8657 // Some browsers fire keypress events for backspace
8658 if (ch == "\x08") { return }
8659 if (handleCharBinding(cm, e, ch)) { return }
8660 cm.display.input.onKeyPress(e);
8661 }
8662
8663 var DOUBLECLICK_DELAY = 400;
8664
8665 var PastClick = function(time, pos, button) {
8666 this.time = time;
8667 this.pos = pos;
8668 this.button = button;
8669 };
8670
8671 PastClick.prototype.compare = function (time, pos, button) {
8672 return this.time + DOUBLECLICK_DELAY > time &&
8673 cmp(pos, this.pos) == 0 && button == this.button
8674 };
8675
8676 var lastClick;
8677 var lastDoubleClick;
8678 function clickRepeat(pos, button) {
8679 var now = +new Date;
8680 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8681 lastClick = lastDoubleClick = null;
8682 return "triple"
8683 } else if (lastClick && lastClick.compare(now, pos, button)) {
8684 lastDoubleClick = new PastClick(now, pos, button);
8685 lastClick = null;
8686 return "double"
8687 } else {
8688 lastClick = new PastClick(now, pos, button);
8689 lastDoubleClick = null;
8690 return "single"
8691 }
8692 }
8693
8694 // A mouse down can be a single click, double click, triple click,
8695 // start of selection drag, start of text drag, new cursor
8696 // (ctrl-click), rectangle drag (alt-drag), or xwin
8697 // middle-click-paste. Or it might be a click on something we should
8698 // not interfere with, such as a scrollbar or widget.
8699 function onMouseDown(e) {
8700 var cm = this, display = cm.display;
8701 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8702 display.input.ensurePolled();
8703 display.shift = e.shiftKey;
8704
8705 if (eventInWidget(display, e)) {
8706 if (!webkit) {
8707 // Briefly turn off draggability, to allow widgets to do
8708 // normal dragging things.
8709 display.scroller.draggable = false;
8710 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8711 }
8712 return
8713 }
8714 if (clickInGutter(cm, e)) { return }
8715 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8716 window.focus();
8717
8718 // #3261: make sure, that we're not starting a second selection
8719 if (button == 1 && cm.state.selectingText)
8720 { cm.state.selectingText(e); }
8721
8722 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8723
8724 if (button == 1) {
8725 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8726 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8727 } else if (button == 2) {
8728 if (pos) { extendSelection(cm.doc, pos); }
8729 setTimeout(function () { return display.input.focus(); }, 20);
8730 } else if (button == 3) {
8731 if (captureRightClick) { onContextMenu(cm, e); }
8732 else { delayBlurEvent(cm); }
8733 }
8734 }
8735
8736 function handleMappedButton(cm, button, pos, repeat, event) {
8737 var name = "Click";
8738 if (repeat == "double") { name = "Double" + name; }
8739 else if (repeat == "triple") { name = "Triple" + name; }
8740 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8741
8742 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8743 if (typeof bound == "string") { bound = commands[bound]; }
8744 if (!bound) { return false }
8745 var done = false;
8746 try {
8747 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8748 done = bound(cm, pos) != Pass;
8749 } finally {
8750 cm.state.suppressEdits = false;
8751 }
8752 return done
8753 })
8754 }
8755
8756 function configureMouse(cm, repeat, event) {
8757 var option = cm.getOption("configureMouse");
8758 var value = option ? option(cm, repeat, event) : {};
8759 if (value.unit == null) {
8760 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8761 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8762 }
8763 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8764 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8765 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8766 return value
8767 }
8768
8769 function leftButtonDown(cm, pos, repeat, event) {
8770 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8771 else { cm.curOp.focus = activeElt(); }
8772
8773 var behavior = configureMouse(cm, repeat, event);
8774
8775 var sel = cm.doc.sel, contained;
8776 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8777 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8778 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8779 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8780 { leftButtonStartDrag(cm, event, pos, behavior); }
8781 else
8782 { leftButtonSelect(cm, event, pos, behavior); }
8783 }
8784
8785 // Start a text drag. When it ends, see if any dragging actually
8786 // happen, and treat as a click if it didn't.
8787 function leftButtonStartDrag(cm, event, pos, behavior) {
8788 var display = cm.display, moved = false;
8789 var dragEnd = operation(cm, function (e) {
8790 if (webkit) { display.scroller.draggable = false; }
8791 cm.state.draggingText = false;
8792 off(document, "mouseup", dragEnd);
8793 off(document, "mousemove", mouseMove);
8794 off(display.scroller, "dragstart", dragStart);
8795 off(display.scroller, "drop", dragEnd);
8796 if (!moved) {
8797 e_preventDefault(e);
8798 if (!behavior.addNew)
8799 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8800 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8801 if (webkit || ie && ie_version == 9)
8802 { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); }
8803 else
8804 { display.input.focus(); }
8805 }
8806 });
8807 var mouseMove = function(e2) {
8808 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8809 };
8810 var dragStart = function () { return moved = true; };
8811 // Let the drag handler handle this.
8812 if (webkit) { display.scroller.draggable = true; }
8813 cm.state.draggingText = dragEnd;
8814 dragEnd.copy = !behavior.moveOnDrag;
8815 // IE's approach to draggable
8816 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8817 on(document, "mouseup", dragEnd);
8818 on(document, "mousemove", mouseMove);
8819 on(display.scroller, "dragstart", dragStart);
8820 on(display.scroller, "drop", dragEnd);
8821
8822 delayBlurEvent(cm);
8823 setTimeout(function () { return display.input.focus(); }, 20);
8824 }
8825
8826 function rangeForUnit(cm, pos, unit) {
8827 if (unit == "char") { return new Range(pos, pos) }
8828 if (unit == "word") { return cm.findWordAt(pos) }
8829 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
8830 var result = unit(cm, pos);
8831 return new Range(result.from, result.to)
8832 }
8833
8834 // Normal selection, as opposed to text dragging.
8835 function leftButtonSelect(cm, event, start, behavior) {
8836 var display = cm.display, doc = cm.doc;
8837 e_preventDefault(event);
8838
8839 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
8840 if (behavior.addNew && !behavior.extend) {
8841 ourIndex = doc.sel.contains(start);
8842 if (ourIndex > -1)
8843 { ourRange = ranges[ourIndex]; }
8844 else
8845 { ourRange = new Range(start, start); }
8846 } else {
8847 ourRange = doc.sel.primary();
8848 ourIndex = doc.sel.primIndex;
8849 }
8850
8851 if (behavior.unit == "rectangle") {
8852 if (!behavior.addNew) { ourRange = new Range(start, start); }
8853 start = posFromMouse(cm, event, true, true);
8854 ourIndex = -1;
8855 } else {
8856 var range$$1 = rangeForUnit(cm, start, behavior.unit);
8857 if (behavior.extend)
8858 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
8859 else
8860 { ourRange = range$$1; }
8861 }
8862
8863 if (!behavior.addNew) {
8864 ourIndex = 0;
8865 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
8866 startSel = doc.sel;
8867 } else if (ourIndex == -1) {
8868 ourIndex = ranges.length;
8869 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
8870 {scroll: false, origin: "*mouse"});
8871 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
8872 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
8873 {scroll: false, origin: "*mouse"});
8874 startSel = doc.sel;
8875 } else {
8876 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
8877 }
8878
8879 var lastPos = start;
8880 function extendTo(pos) {
8881 if (cmp(lastPos, pos) == 0) { return }
8882 lastPos = pos;
8883
8884 if (behavior.unit == "rectangle") {
8885 var ranges = [], tabSize = cm.options.tabSize;
8886 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
8887 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
8888 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
8889 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
8890 line <= end; line++) {
8891 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
8892 if (left == right)
8893 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
8894 else if (text.length > leftPos)
8895 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
8896 }
8897 if (!ranges.length) { ranges.push(new Range(start, start)); }
8898 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
8899 {origin: "*mouse", scroll: false});
8900 cm.scrollIntoView(pos);
8901 } else {
8902 var oldRange = ourRange;
8903 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
8904 var anchor = oldRange.anchor, head;
8905 if (cmp(range$$1.anchor, anchor) > 0) {
8906 head = range$$1.head;
8907 anchor = minPos(oldRange.from(), range$$1.anchor);
8908 } else {
8909 head = range$$1.anchor;
8910 anchor = maxPos(oldRange.to(), range$$1.head);
8911 }
8912 var ranges$1 = startSel.ranges.slice(0);
8913 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
8914 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse);
8915 }
8916 }
8917
8918 var editorSize = display.wrapper.getBoundingClientRect();
8919 // Used to ensure timeout re-tries don't fire when another extend
8920 // happened in the meantime (clearTimeout isn't reliable -- at
8921 // least on Chrome, the timeouts still happen even when cleared,
8922 // if the clear happens after their scheduled firing time).
8923 var counter = 0;
8924
8925 function extend(e) {
8926 var curCount = ++counter;
8927 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
8928 if (!cur) { return }
8929 if (cmp(cur, lastPos) != 0) {
8930 cm.curOp.focus = activeElt();
8931 extendTo(cur);
8932 var visible = visibleLines(display, doc);
8933 if (cur.line >= visible.to || cur.line < visible.from)
8934 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
8935 } else {
8936 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
8937 if (outside) { setTimeout(operation(cm, function () {
8938 if (counter != curCount) { return }
8939 display.scroller.scrollTop += outside;
8940 extend(e);
8941 }), 50); }
8942 }
8943 }
8944
8945 function done(e) {
8946 cm.state.selectingText = false;
8947 counter = Infinity;
8948 e_preventDefault(e);
8949 display.input.focus();
8950 off(document, "mousemove", move);
8951 off(document, "mouseup", up);
8952 doc.history.lastSelOrigin = null;
8953 }
8954
8955 var move = operation(cm, function (e) {
8956 if (!e_button(e)) { done(e); }
8957 else { extend(e); }
8958 });
8959 var up = operation(cm, done);
8960 cm.state.selectingText = up;
8961 on(document, "mousemove", move);
8962 on(document, "mouseup", up);
8963 }
8964
8965 // Used when mouse-selecting to adjust the anchor to the proper side
8966 // of a bidi jump depending on the visual position of the head.
8967 function bidiSimplify(cm, range$$1) {
8968 var anchor = range$$1.anchor;
8969 var head = range$$1.head;
8970 var anchorLine = getLine(cm.doc, anchor.line);
8971 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
8972 var order = getOrder(anchorLine);
8973 if (!order) { return range$$1 }
8974 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
8975 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
8976 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
8977 if (boundary == 0 || boundary == order.length) { return range$$1 }
8978
8979 // Compute the relative visual position of the head compared to the
8980 // anchor (<0 is to the left, >0 to the right)
8981 var leftSide;
8982 if (head.line != anchor.line) {
8983 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
8984 } else {
8985 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
8986 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
8987 if (headIndex == boundary - 1 || headIndex == boundary)
8988 { leftSide = dir < 0; }
8989 else
8990 { leftSide = dir > 0; }
8991 }
8992
8993 var usePart = order[boundary + (leftSide ? -1 : 0)];
8994 var from = leftSide == (usePart.level == 1);
8995 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
8996 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
8997 }
8998
8999
9000 // Determines whether an event happened in the gutter, and fires the
9001 // handlers for the corresponding event.
9002 function gutterEvent(cm, e, type, prevent) {
9003 var mX, mY;
9004 if (e.touches) {
9005 mX = e.touches[0].clientX;
9006 mY = e.touches[0].clientY;
9007 } else {
9008 try { mX = e.clientX; mY = e.clientY; }
9009 catch(e) { return false }
9010 }
9011 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9012 if (prevent) { e_preventDefault(e); }
9013
9014 var display = cm.display;
9015 var lineBox = display.lineDiv.getBoundingClientRect();
9016
9017 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9018 mY -= lineBox.top - display.viewOffset;
9019
9020 for (var i = 0; i < cm.options.gutters.length; ++i) {
9021 var g = display.gutters.childNodes[i];
9022 if (g && g.getBoundingClientRect().right >= mX) {
9023 var line = lineAtHeight(cm.doc, mY);
9024 var gutter = cm.options.gutters[i];
9025 signal(cm, type, cm, line, gutter, e);
9026 return e_defaultPrevented(e)
9027 }
9028 }
9029 }
9030
9031 function clickInGutter(cm, e) {
9032 return gutterEvent(cm, e, "gutterClick", true)
9033 }
9034
9035 // CONTEXT MENU HANDLING
9036
9037 // To make the context menu work, we need to briefly unhide the
9038 // textarea (making it as unobtrusive as possible) to let the
9039 // right-click take effect on it.
9040 function onContextMenu(cm, e) {
9041 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9042 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9043 cm.display.input.onContextMenu(e);
9044 }
9045
9046 function contextMenuInGutter(cm, e) {
9047 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9048 return gutterEvent(cm, e, "gutterContextMenu", false)
9049 }
9050
9051 function themeChanged(cm) {
9052 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9053 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9054 clearCaches(cm);
9055 }
9056
9057 var Init = {toString: function(){return "CodeMirror.Init"}};
9058
9059 var defaults = {};
9060 var optionHandlers = {};
9061
9062 function defineOptions(CodeMirror) {
9063 var optionHandlers = CodeMirror.optionHandlers;
9064
9065 function option(name, deflt, handle, notOnInit) {
9066 CodeMirror.defaults[name] = deflt;
9067 if (handle) { optionHandlers[name] =
9068 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9069 }
9070
9071 CodeMirror.defineOption = option;
9072
9073 // Passed to option handlers when there is no old value.
9074 CodeMirror.Init = Init;
9075
9076 // These two are, on init, called from the constructor because they
9077 // have to be initialized before the editor can start at all.
9078 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9079 option("mode", null, function (cm, val) {
9080 cm.doc.modeOption = val;
9081 loadMode(cm);
9082 }, true);
9083
9084 option("indentUnit", 2, loadMode, true);
9085 option("indentWithTabs", false);
9086 option("smartIndent", true);
9087 option("tabSize", 4, function (cm) {
9088 resetModeState(cm);
9089 clearCaches(cm);
9090 regChange(cm);
9091 }, true);
9092
9093 option("lineSeparator", null, function (cm, val) {
9094 cm.doc.lineSep = val;
9095 if (!val) { return }
9096 var newBreaks = [], lineNo = cm.doc.first;
9097 cm.doc.iter(function (line) {
9098 for (var pos = 0;;) {
9099 var found = line.text.indexOf(val, pos);
9100 if (found == -1) { break }
9101 pos = found + val.length;
9102 newBreaks.push(Pos(lineNo, found));
9103 }
9104 lineNo++;
9105 });
9106 for (var i = newBreaks.length - 1; i >= 0; i--)
9107 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9108 });
9109 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
9110 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9111 if (old != Init) { cm.refresh(); }
9112 });
9113 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9114 option("electricChars", true);
9115 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9116 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9117 }, true);
9118 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9119 option("rtlMoveVisually", !windows);
9120 option("wholeLineUpdateBefore", true);
9121
9122 option("theme", "default", function (cm) {
9123 themeChanged(cm);
9124 guttersChanged(cm);
9125 }, true);
9126 option("keyMap", "default", function (cm, val, old) {
9127 var next = getKeyMap(val);
9128 var prev = old != Init && getKeyMap(old);
9129 if (prev && prev.detach) { prev.detach(cm, next); }
9130 if (next.attach) { next.attach(cm, prev || null); }
9131 });
9132 option("extraKeys", null);
9133 option("configureMouse", null);
9134
9135 option("lineWrapping", false, wrappingChanged, true);
9136 option("gutters", [], function (cm) {
9137 setGuttersForLineNumbers(cm.options);
9138 guttersChanged(cm);
9139 }, true);
9140 option("fixedGutter", true, function (cm, val) {
9141 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9142 cm.refresh();
9143 }, true);
9144 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9145 option("scrollbarStyle", "native", function (cm) {
9146 initScrollbars(cm);
9147 updateScrollbars(cm);
9148 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9149 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9150 }, true);
9151 option("lineNumbers", false, function (cm) {
9152 setGuttersForLineNumbers(cm.options);
9153 guttersChanged(cm);
9154 }, true);
9155 option("firstLineNumber", 1, guttersChanged, true);
9156 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
9157 option("showCursorWhenSelecting", false, updateSelection, true);
9158
9159 option("resetSelectionOnContextMenu", true);
9160 option("lineWiseCopyCut", true);
9161 option("pasteLinesPerSelection", true);
9162
9163 option("readOnly", false, function (cm, val) {
9164 if (val == "nocursor") {
9165 onBlur(cm);
9166 cm.display.input.blur();
9167 }
9168 cm.display.input.readOnlyChanged(val);
9169 });
9170 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9171 option("dragDrop", true, dragDropChanged);
9172 option("allowDropFileTypes", null);
9173
9174 option("cursorBlinkRate", 530);
9175 option("cursorScrollMargin", 0);
9176 option("cursorHeight", 1, updateSelection, true);
9177 option("singleCursorHeightPerLine", true, updateSelection, true);
9178 option("workTime", 100);
9179 option("workDelay", 100);
9180 option("flattenSpans", true, resetModeState, true);
9181 option("addModeClass", false, resetModeState, true);
9182 option("pollInterval", 100);
9183 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9184 option("historyEventDelay", 1250);
9185 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9186 option("maxHighlightLength", 10000, resetModeState, true);
9187 option("moveInputWithCursor", true, function (cm, val) {
9188 if (!val) { cm.display.input.resetPosition(); }
9189 });
9190
9191 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9192 option("autofocus", null);
9193 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9194 }
9195
9196 function guttersChanged(cm) {
9197 updateGutters(cm);
9198 regChange(cm);
9199 alignHorizontally(cm);
9200 }
9201
9202 function dragDropChanged(cm, value, old) {
9203 var wasOn = old && old != Init;
9204 if (!value != !wasOn) {
9205 var funcs = cm.display.dragFunctions;
9206 var toggle = value ? on : off;
9207 toggle(cm.display.scroller, "dragstart", funcs.start);
9208 toggle(cm.display.scroller, "dragenter", funcs.enter);
9209 toggle(cm.display.scroller, "dragover", funcs.over);
9210 toggle(cm.display.scroller, "dragleave", funcs.leave);
9211 toggle(cm.display.scroller, "drop", funcs.drop);
9212 }
9213 }
9214
9215 function wrappingChanged(cm) {
9216 if (cm.options.lineWrapping) {
9217 addClass(cm.display.wrapper, "CodeMirror-wrap");
9218 cm.display.sizer.style.minWidth = "";
9219 cm.display.sizerWidth = null;
9220 } else {
9221 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9222 findMaxLine(cm);
9223 }
9224 estimateLineHeights(cm);
9225 regChange(cm);
9226 clearCaches(cm);
9227 setTimeout(function () { return updateScrollbars(cm); }, 100);
9228 }
9229
9230 // A CodeMirror instance represents an editor. This is the object
9231 // that user code is usually dealing with.
9232
9233 function CodeMirror$1(place, options) {
9234 var this$1 = this;
9235
9236 if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
9237
9238 this.options = options = options ? copyObj(options) : {};
9239 // Determine effective options based on given values and defaults.
9240 copyObj(defaults, options, false);
9241 setGuttersForLineNumbers(options);
9242
9243 var doc = options.value;
9244 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9245 this.doc = doc;
9246
9247 var input = new CodeMirror$1.inputStyles[options.inputStyle](this);
9248 var display = this.display = new Display(place, doc, input);
9249 display.wrapper.CodeMirror = this;
9250 updateGutters(this);
9251 themeChanged(this);
9252 if (options.lineWrapping)
9253 { this.display.wrapper.className += " CodeMirror-wrap"; }
9254 initScrollbars(this);
9255
9256 this.state = {
9257 keyMaps: [], // stores maps added by addKeyMap
9258 overlays: [], // highlighting overlays, as added by addOverlay
9259 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9260 overwrite: false,
9261 delayingBlurEvent: false,
9262 focused: false,
9263 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9264 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
9265 selectingText: false,
9266 draggingText: false,
9267 highlight: new Delayed(), // stores highlight worker timeout
9268 keySeq: null, // Unfinished key sequence
9269 specialChars: null
9270 };
9271
9272 if (options.autofocus && !mobile) { display.input.focus(); }
9273
9274 // Override magic textarea content restore that IE sometimes does
9275 // on our hidden textarea on reload
9276 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9277
9278 registerEventHandlers(this);
9279 ensureGlobalHandlers();
9280
9281 startOperation(this);
9282 this.curOp.forceUpdate = true;
9283 attachDoc(this, doc);
9284
9285 if ((options.autofocus && !mobile) || this.hasFocus())
9286 { setTimeout(bind(onFocus, this), 20); }
9287 else
9288 { onBlur(this); }
9289
9290 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9291 { optionHandlers[opt](this$1, options[opt], Init); } }
9292 maybeUpdateLineNumberWidth(this);
9293 if (options.finishInit) { options.finishInit(this); }
9294 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9295 endOperation(this);
9296 // Suppress optimizelegibility in Webkit, since it breaks text
9297 // measuring on line wrapping boundaries.
9298 if (webkit && options.lineWrapping &&
9299 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9300 { display.lineDiv.style.textRendering = "auto"; }
9301 }
9302
9303 // The default configuration options.
9304 CodeMirror$1.defaults = defaults;
9305 // Functions to run when options are changed.
9306 CodeMirror$1.optionHandlers = optionHandlers;
9307
9308 // Attach the necessary event handlers when initializing the editor
9309 function registerEventHandlers(cm) {
9310 var d = cm.display;
9311 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9312 // Older IE's will not fire a second mousedown for a double click
9313 if (ie && ie_version < 11)
9314 { on(d.scroller, "dblclick", operation(cm, function (e) {
9315 if (signalDOMEvent(cm, e)) { return }
9316 var pos = posFromMouse(cm, e);
9317 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9318 e_preventDefault(e);
9319 var word = cm.findWordAt(pos);
9320 extendSelection(cm.doc, word.anchor, word.head);
9321 })); }
9322 else
9323 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9324 // Some browsers fire contextmenu *after* opening the menu, at
9325 // which point we can't mess with it anymore. Context menu is
9326 // handled in onMouseDown for these browsers.
9327 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); }
9328
9329 // Used to suppress mouse event handling when a touch happens
9330 var touchFinished, prevTouch = {end: 0};
9331 function finishTouch() {
9332 if (d.activeTouch) {
9333 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9334 prevTouch = d.activeTouch;
9335 prevTouch.end = +new Date;
9336 }
9337 }
9338 function isMouseLikeTouchEvent(e) {
9339 if (e.touches.length != 1) { return false }
9340 var touch = e.touches[0];
9341 return touch.radiusX <= 1 && touch.radiusY <= 1
9342 }
9343 function farAway(touch, other) {
9344 if (other.left == null) { return true }
9345 var dx = other.left - touch.left, dy = other.top - touch.top;
9346 return dx * dx + dy * dy > 20 * 20
9347 }
9348 on(d.scroller, "touchstart", function (e) {
9349 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9350 d.input.ensurePolled();
9351 clearTimeout(touchFinished);
9352 var now = +new Date;
9353 d.activeTouch = {start: now, moved: false,
9354 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9355 if (e.touches.length == 1) {
9356 d.activeTouch.left = e.touches[0].pageX;
9357 d.activeTouch.top = e.touches[0].pageY;
9358 }
9359 }
9360 });
9361 on(d.scroller, "touchmove", function () {
9362 if (d.activeTouch) { d.activeTouch.moved = true; }
9363 });
9364 on(d.scroller, "touchend", function (e) {
9365 var touch = d.activeTouch;
9366 if (touch && !eventInWidget(d, e) && touch.left != null &&
9367 !touch.moved && new Date - touch.start < 300) {
9368 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9369 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9370 { range = new Range(pos, pos); }
9371 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9372 { range = cm.findWordAt(pos); }
9373 else // Triple tap
9374 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9375 cm.setSelection(range.anchor, range.head);
9376 cm.focus();
9377 e_preventDefault(e);
9378 }
9379 finishTouch();
9380 });
9381 on(d.scroller, "touchcancel", finishTouch);
9382
9383 // Sync scrolling between fake scrollbars and real scrollable
9384 // area, ensure viewport is updated when scrolling.
9385 on(d.scroller, "scroll", function () {
9386 if (d.scroller.clientHeight) {
9387 updateScrollTop(cm, d.scroller.scrollTop);
9388 setScrollLeft(cm, d.scroller.scrollLeft, true);
9389 signal(cm, "scroll", cm);
9390 }
9391 });
9392
9393 // Listen to wheel events in order to try and update the viewport on time.
9394 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9395 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9396
9397 // Prevent wrapper from ever scrolling
9398 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9399
9400 d.dragFunctions = {
9401 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9402 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9403 start: function (e) { return onDragStart(cm, e); },
9404 drop: operation(cm, onDrop),
9405 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9406 };
9407
9408 var inp = d.input.getField();
9409 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9410 on(inp, "keydown", operation(cm, onKeyDown));
9411 on(inp, "keypress", operation(cm, onKeyPress));
9412 on(inp, "focus", function (e) { return onFocus(cm, e); });
9413 on(inp, "blur", function (e) { return onBlur(cm, e); });
9414 }
9415
9416 var initHooks = [];
9417 CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); };
9418
9419 // Indent the given line. The how parameter can be "smart",
9420 // "add"/null, "subtract", or "prev". When aggressive is false
9421 // (typically set to true for forced single-line indents), empty
9422 // lines are not indented, and places where the mode returns Pass
9423 // are left alone.
9424 function indentLine(cm, n, how, aggressive) {
9425 var doc = cm.doc, state;
9426 if (how == null) { how = "add"; }
9427 if (how == "smart") {
9428 // Fall back to "prev" when the mode doesn't have an indentation
9429 // method.
9430 if (!doc.mode.indent) { how = "prev"; }
9431 else { state = getContextBefore(cm, n).state; }
9432 }
9433
9434 var tabSize = cm.options.tabSize;
9435 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9436 if (line.stateAfter) { line.stateAfter = null; }
9437 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9438 if (!aggressive && !/\S/.test(line.text)) {
9439 indentation = 0;
9440 how = "not";
9441 } else if (how == "smart") {
9442 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9443 if (indentation == Pass || indentation > 150) {
9444 if (!aggressive) { return }
9445 how = "prev";
9446 }
9447 }
9448 if (how == "prev") {
9449 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9450 else { indentation = 0; }
9451 } else if (how == "add") {
9452 indentation = curSpace + cm.options.indentUnit;
9453 } else if (how == "subtract") {
9454 indentation = curSpace - cm.options.indentUnit;
9455 } else if (typeof how == "number") {
9456 indentation = curSpace + how;
9457 }
9458 indentation = Math.max(0, indentation);
9459
9460 var indentString = "", pos = 0;
9461 if (cm.options.indentWithTabs)
9462 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9463 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9464
9465 if (indentString != curSpaceString) {
9466 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9467 line.stateAfter = null;
9468 return true
9469 } else {
9470 // Ensure that, if the cursor was in the whitespace at the start
9471 // of the line, it is moved to the end of that space.
9472 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9473 var range = doc.sel.ranges[i$1];
9474 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9475 var pos$1 = Pos(n, curSpaceString.length);
9476 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9477 break
9478 }
9479 }
9480 }
9481 }
9482
9483 // This will be set to a {lineWise: bool, text: [string]} object, so
9484 // that, when pasting, we know what kind of selections the copied
9485 // text was made out of.
9486 var lastCopied = null;
9487
9488 function setLastCopied(newLastCopied) {
9489 lastCopied = newLastCopied;
9490 }
9491
9492 function applyTextInput(cm, inserted, deleted, sel, origin) {
9493 var doc = cm.doc;
9494 cm.display.shift = false;
9495 if (!sel) { sel = doc.sel; }
9496
9497 var paste = cm.state.pasteIncoming || origin == "paste";
9498 var textLines = splitLinesAuto(inserted), multiPaste = null;
9499 // When pasting N lines into N selections, insert one line per selection
9500 if (paste && sel.ranges.length > 1) {
9501 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9502 if (sel.ranges.length % lastCopied.text.length == 0) {
9503 multiPaste = [];
9504 for (var i = 0; i < lastCopied.text.length; i++)
9505 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9506 }
9507 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9508 multiPaste = map(textLines, function (l) { return [l]; });
9509 }
9510 }
9511
9512 var updateInput;
9513 // Normal behavior is to insert the new text into every selection
9514 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9515 var range$$1 = sel.ranges[i$1];
9516 var from = range$$1.from(), to = range$$1.to();
9517 if (range$$1.empty()) {
9518 if (deleted && deleted > 0) // Handle deletion
9519 { from = Pos(from.line, from.ch - deleted); }
9520 else if (cm.state.overwrite && !paste) // Handle overwrite
9521 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9522 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9523 { from = to = Pos(from.line, 0); }
9524 }
9525 updateInput = cm.curOp.updateInput;
9526 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9527 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
9528 makeChange(cm.doc, changeEvent);
9529 signalLater(cm, "inputRead", cm, changeEvent);
9530 }
9531 if (inserted && !paste)
9532 { triggerElectric(cm, inserted); }
9533
9534 ensureCursorVisible(cm);
9535 cm.curOp.updateInput = updateInput;
9536 cm.curOp.typing = true;
9537 cm.state.pasteIncoming = cm.state.cutIncoming = false;
9538 }
9539
9540 function handlePaste(e, cm) {
9541 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9542 if (pasted) {
9543 e.preventDefault();
9544 if (!cm.isReadOnly() && !cm.options.disableInput)
9545 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9546 return true
9547 }
9548 }
9549
9550 function triggerElectric(cm, inserted) {
9551 // When an 'electric' character is inserted, immediately trigger a reindent
9552 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9553 var sel = cm.doc.sel;
9554
9555 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9556 var range$$1 = sel.ranges[i];
9557 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9558 var mode = cm.getModeAt(range$$1.head);
9559 var indented = false;
9560 if (mode.electricChars) {
9561 for (var j = 0; j < mode.electricChars.length; j++)
9562 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9563 indented = indentLine(cm, range$$1.head.line, "smart");
9564 break
9565 } }
9566 } else if (mode.electricInput) {
9567 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9568 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9569 }
9570 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9571 }
9572 }
9573
9574 function copyableRanges(cm) {
9575 var text = [], ranges = [];
9576 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9577 var line = cm.doc.sel.ranges[i].head.line;
9578 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9579 ranges.push(lineRange);
9580 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9581 }
9582 return {text: text, ranges: ranges}
9583 }
9584
9585 function disableBrowserMagic(field, spellcheck) {
9586 field.setAttribute("autocorrect", "off");
9587 field.setAttribute("autocapitalize", "off");
9588 field.setAttribute("spellcheck", !!spellcheck);
9589 }
9590
9591 function hiddenTextarea() {
9592 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9593 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9594 // The textarea is kept positioned near the cursor to prevent the
9595 // fact that it'll be scrolled into view on input from scrolling
9596 // our fake cursor out of view. On webkit, when wrap=off, paste is
9597 // very slow. So make the area wide instead.
9598 if (webkit) { te.style.width = "1000px"; }
9599 else { te.setAttribute("wrap", "off"); }
9600 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9601 if (ios) { te.style.border = "1px solid black"; }
9602 disableBrowserMagic(te);
9603 return div
9604 }
9605
9606 // The publicly visible API. Note that methodOp(f) means
9607 // 'wrap f in an operation, performed on its `this` parameter'.
9608
9609 // This is not the complete set of editor methods. Most of the
9610 // methods defined on the Doc type are also injected into
9611 // CodeMirror.prototype, for backwards compatibility and
9612 // convenience.
9613
9614 var addEditorMethods = function(CodeMirror) {
9615 var optionHandlers = CodeMirror.optionHandlers;
9616
9617 var helpers = CodeMirror.helpers = {};
9618
9619 CodeMirror.prototype = {
9620 constructor: CodeMirror,
9621 focus: function(){window.focus(); this.display.input.focus();},
9622
9623 setOption: function(option, value) {
9624 var options = this.options, old = options[option];
9625 if (options[option] == value && option != "mode") { return }
9626 options[option] = value;
9627 if (optionHandlers.hasOwnProperty(option))
9628 { operation(this, optionHandlers[option])(this, value, old); }
9629 signal(this, "optionChange", this, option);
9630 },
9631
9632 getOption: function(option) {return this.options[option]},
9633 getDoc: function() {return this.doc},
9634
9635 addKeyMap: function(map$$1, bottom) {
9636 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9637 },
9638 removeKeyMap: function(map$$1) {
9639 var maps = this.state.keyMaps;
9640 for (var i = 0; i < maps.length; ++i)
9641 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9642 maps.splice(i, 1);
9643 return true
9644 } }
9645 },
9646
9647 addOverlay: methodOp(function(spec, options) {
9648 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9649 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9650 insertSorted(this.state.overlays,
9651 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9652 priority: (options && options.priority) || 0},
9653 function (overlay) { return overlay.priority; });
9654 this.state.modeGen++;
9655 regChange(this);
9656 }),
9657 removeOverlay: methodOp(function(spec) {
9658 var this$1 = this;
9659
9660 var overlays = this.state.overlays;
9661 for (var i = 0; i < overlays.length; ++i) {
9662 var cur = overlays[i].modeSpec;
9663 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9664 overlays.splice(i, 1);
9665 this$1.state.modeGen++;
9666 regChange(this$1);
9667 return
9668 }
9669 }
9670 }),
9671
9672 indentLine: methodOp(function(n, dir, aggressive) {
9673 if (typeof dir != "string" && typeof dir != "number") {
9674 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9675 else { dir = dir ? "add" : "subtract"; }
9676 }
9677 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9678 }),
9679 indentSelection: methodOp(function(how) {
9680 var this$1 = this;
9681
9682 var ranges = this.doc.sel.ranges, end = -1;
9683 for (var i = 0; i < ranges.length; i++) {
9684 var range$$1 = ranges[i];
9685 if (!range$$1.empty()) {
9686 var from = range$$1.from(), to = range$$1.to();
9687 var start = Math.max(end, from.line);
9688 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9689 for (var j = start; j < end; ++j)
9690 { indentLine(this$1, j, how); }
9691 var newRanges = this$1.doc.sel.ranges;
9692 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9693 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9694 } else if (range$$1.head.line > end) {
9695 indentLine(this$1, range$$1.head.line, how, true);
9696 end = range$$1.head.line;
9697 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9698 }
9699 }
9700 }),
9701
9702 // Fetch the parser token for a given character. Useful for hacks
9703 // that want to inspect the mode state (say, for completion).
9704 getTokenAt: function(pos, precise) {
9705 return takeToken(this, pos, precise)
9706 },
9707
9708 getLineTokens: function(line, precise) {
9709 return takeToken(this, Pos(line), precise, true)
9710 },
9711
9712 getTokenTypeAt: function(pos) {
9713 pos = clipPos(this.doc, pos);
9714 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9715 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9716 var type;
9717 if (ch == 0) { type = styles[2]; }
9718 else { for (;;) {
9719 var mid = (before + after) >> 1;
9720 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9721 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9722 else { type = styles[mid * 2 + 2]; break }
9723 } }
9724 var cut = type ? type.indexOf("overlay ") : -1;
9725 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9726 },
9727
9728 getModeAt: function(pos) {
9729 var mode = this.doc.mode;
9730 if (!mode.innerMode) { return mode }
9731 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9732 },
9733
9734 getHelper: function(pos, type) {
9735 return this.getHelpers(pos, type)[0]
9736 },
9737
9738 getHelpers: function(pos, type) {
9739 var this$1 = this;
9740
9741 var found = [];
9742 if (!helpers.hasOwnProperty(type)) { return found }
9743 var help = helpers[type], mode = this.getModeAt(pos);
9744 if (typeof mode[type] == "string") {
9745 if (help[mode[type]]) { found.push(help[mode[type]]); }
9746 } else if (mode[type]) {
9747 for (var i = 0; i < mode[type].length; i++) {
9748 var val = help[mode[type][i]];
9749 if (val) { found.push(val); }
9750 }
9751 } else if (mode.helperType && help[mode.helperType]) {
9752 found.push(help[mode.helperType]);
9753 } else if (help[mode.name]) {
9754 found.push(help[mode.name]);
9755 }
9756 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9757 var cur = help._global[i$1];
9758 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9759 { found.push(cur.val); }
9760 }
9761 return found
9762 },
9763
9764 getStateAfter: function(line, precise) {
9765 var doc = this.doc;
9766 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9767 return getContextBefore(this, line + 1, precise).state
9768 },
9769
9770 cursorCoords: function(start, mode) {
9771 var pos, range$$1 = this.doc.sel.primary();
9772 if (start == null) { pos = range$$1.head; }
9773 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9774 else { pos = start ? range$$1.from() : range$$1.to(); }
9775 return cursorCoords(this, pos, mode || "page")
9776 },
9777
9778 charCoords: function(pos, mode) {
9779 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9780 },
9781
9782 coordsChar: function(coords, mode) {
9783 coords = fromCoordSystem(this, coords, mode || "page");
9784 return coordsChar(this, coords.left, coords.top)
9785 },
9786
9787 lineAtHeight: function(height, mode) {
9788 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9789 return lineAtHeight(this.doc, height + this.display.viewOffset)
9790 },
9791 heightAtLine: function(line, mode, includeWidgets) {
9792 var end = false, lineObj;
9793 if (typeof line == "number") {
9794 var last = this.doc.first + this.doc.size - 1;
9795 if (line < this.doc.first) { line = this.doc.first; }
9796 else if (line > last) { line = last; end = true; }
9797 lineObj = getLine(this.doc, line);
9798 } else {
9799 lineObj = line;
9800 }
9801 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9802 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9803 },
9804
9805 defaultTextHeight: function() { return textHeight(this.display) },
9806 defaultCharWidth: function() { return charWidth(this.display) },
9807
9808 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9809
9810 addWidget: function(pos, node, scroll, vert, horiz) {
9811 var display = this.display;
9812 pos = cursorCoords(this, clipPos(this.doc, pos));
9813 var top = pos.bottom, left = pos.left;
9814 node.style.position = "absolute";
9815 node.setAttribute("cm-ignore-events", "true");
9816 this.display.input.setUneditable(node);
9817 display.sizer.appendChild(node);
9818 if (vert == "over") {
9819 top = pos.top;
9820 } else if (vert == "above" || vert == "near") {
9821 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9822 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
9823 // Default to positioning above (if specified and possible); otherwise default to positioning below
9824 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
9825 { top = pos.top - node.offsetHeight; }
9826 else if (pos.bottom + node.offsetHeight <= vspace)
9827 { top = pos.bottom; }
9828 if (left + node.offsetWidth > hspace)
9829 { left = hspace - node.offsetWidth; }
9830 }
9831 node.style.top = top + "px";
9832 node.style.left = node.style.right = "";
9833 if (horiz == "right") {
9834 left = display.sizer.clientWidth - node.offsetWidth;
9835 node.style.right = "0px";
9836 } else {
9837 if (horiz == "left") { left = 0; }
9838 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
9839 node.style.left = left + "px";
9840 }
9841 if (scroll)
9842 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
9843 },
9844
9845 triggerOnKeyDown: methodOp(onKeyDown),
9846 triggerOnKeyPress: methodOp(onKeyPress),
9847 triggerOnKeyUp: onKeyUp,
9848 triggerOnMouseDown: methodOp(onMouseDown),
9849
9850 execCommand: function(cmd) {
9851 if (commands.hasOwnProperty(cmd))
9852 { return commands[cmd].call(null, this) }
9853 },
9854
9855 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
9856
9857 findPosH: function(from, amount, unit, visually) {
9858 var this$1 = this;
9859
9860 var dir = 1;
9861 if (amount < 0) { dir = -1; amount = -amount; }
9862 var cur = clipPos(this.doc, from);
9863 for (var i = 0; i < amount; ++i) {
9864 cur = findPosH(this$1.doc, cur, dir, unit, visually);
9865 if (cur.hitSide) { break }
9866 }
9867 return cur
9868 },
9869
9870 moveH: methodOp(function(dir, unit) {
9871 var this$1 = this;
9872
9873 this.extendSelectionsBy(function (range$$1) {
9874 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
9875 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
9876 else
9877 { return dir < 0 ? range$$1.from() : range$$1.to() }
9878 }, sel_move);
9879 }),
9880
9881 deleteH: methodOp(function(dir, unit) {
9882 var sel = this.doc.sel, doc = this.doc;
9883 if (sel.somethingSelected())
9884 { doc.replaceSelection("", null, "+delete"); }
9885 else
9886 { deleteNearSelection(this, function (range$$1) {
9887 var other = findPosH(doc, range$$1.head, dir, unit, false);
9888 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
9889 }); }
9890 }),
9891
9892 findPosV: function(from, amount, unit, goalColumn) {
9893 var this$1 = this;
9894
9895 var dir = 1, x = goalColumn;
9896 if (amount < 0) { dir = -1; amount = -amount; }
9897 var cur = clipPos(this.doc, from);
9898 for (var i = 0; i < amount; ++i) {
9899 var coords = cursorCoords(this$1, cur, "div");
9900 if (x == null) { x = coords.left; }
9901 else { coords.left = x; }
9902 cur = findPosV(this$1, coords, dir, unit);
9903 if (cur.hitSide) { break }
9904 }
9905 return cur
9906 },
9907
9908 moveV: methodOp(function(dir, unit) {
9909 var this$1 = this;
9910
9911 var doc = this.doc, goals = [];
9912 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
9913 doc.extendSelectionsBy(function (range$$1) {
9914 if (collapse)
9915 { return dir < 0 ? range$$1.from() : range$$1.to() }
9916 var headPos = cursorCoords(this$1, range$$1.head, "div");
9917 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
9918 goals.push(headPos.left);
9919 var pos = findPosV(this$1, headPos, dir, unit);
9920 if (unit == "page" && range$$1 == doc.sel.primary())
9921 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
9922 return pos
9923 }, sel_move);
9924 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
9925 { doc.sel.ranges[i].goalColumn = goals[i]; } }
9926 }),
9927
9928 // Find the word at the given position (as returned by coordsChar).
9929 findWordAt: function(pos) {
9930 var doc = this.doc, line = getLine(doc, pos.line).text;
9931 var start = pos.ch, end = pos.ch;
9932 if (line) {
9933 var helper = this.getHelper(pos, "wordChars");
9934 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
9935 var startChar = line.charAt(start);
9936 var check = isWordChar(startChar, helper)
9937 ? function (ch) { return isWordChar(ch, helper); }
9938 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
9939 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
9940 while (start > 0 && check(line.charAt(start - 1))) { --start; }
9941 while (end < line.length && check(line.charAt(end))) { ++end; }
9942 }
9943 return new Range(Pos(pos.line, start), Pos(pos.line, end))
9944 },
9945
9946 toggleOverwrite: function(value) {
9947 if (value != null && value == this.state.overwrite) { return }
9948 if (this.state.overwrite = !this.state.overwrite)
9949 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
9950 else
9951 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
9952
9953 signal(this, "overwriteToggle", this, this.state.overwrite);
9954 },
9955 hasFocus: function() { return this.display.input.getField() == activeElt() },
9956 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
9957
9958 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
9959 getScrollInfo: function() {
9960 var scroller = this.display.scroller;
9961 return {left: scroller.scrollLeft, top: scroller.scrollTop,
9962 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
9963 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
9964 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
9965 },
9966
9967 scrollIntoView: methodOp(function(range$$1, margin) {
9968 if (range$$1 == null) {
9969 range$$1 = {from: this.doc.sel.primary().head, to: null};
9970 if (margin == null) { margin = this.options.cursorScrollMargin; }
9971 } else if (typeof range$$1 == "number") {
9972 range$$1 = {from: Pos(range$$1, 0), to: null};
9973 } else if (range$$1.from == null) {
9974 range$$1 = {from: range$$1, to: null};
9975 }
9976 if (!range$$1.to) { range$$1.to = range$$1.from; }
9977 range$$1.margin = margin || 0;
9978
9979 if (range$$1.from.line != null) {
9980 scrollToRange(this, range$$1);
9981 } else {
9982 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
9983 }
9984 }),
9985
9986 setSize: methodOp(function(width, height) {
9987 var this$1 = this;
9988
9989 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
9990 if (width != null) { this.display.wrapper.style.width = interpret(width); }
9991 if (height != null) { this.display.wrapper.style.height = interpret(height); }
9992 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
9993 var lineNo$$1 = this.display.viewFrom;
9994 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
9995 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
9996 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
9997 ++lineNo$$1;
9998 });
9999 this.curOp.forceUpdate = true;
10000 signal(this, "refresh", this);
10001 }),
10002
10003 operation: function(f){return runInOp(this, f)},
10004 startOperation: function(){return startOperation(this)},
10005 endOperation: function(){return endOperation(this)},
10006
10007 refresh: methodOp(function() {
10008 var oldHeight = this.display.cachedTextHeight;
10009 regChange(this);
10010 this.curOp.forceUpdate = true;
10011 clearCaches(this);
10012 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10013 updateGutterSpace(this);
10014 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
10015 { estimateLineHeights(this); }
10016 signal(this, "refresh", this);
10017 }),
10018
10019 swapDoc: methodOp(function(doc) {
10020 var old = this.doc;
10021 old.cm = null;
10022 attachDoc(this, doc);
10023 clearCaches(this);
10024 this.display.input.reset();
10025 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10026 this.curOp.forceScroll = true;
10027 signalLater(this, "swapDoc", this, old);
10028 return old
10029 }),
10030
10031 getInputField: function(){return this.display.input.getField()},
10032 getWrapperElement: function(){return this.display.wrapper},
10033 getScrollerElement: function(){return this.display.scroller},
10034 getGutterElement: function(){return this.display.gutters}
10035 };
10036 eventMixin(CodeMirror);
10037
10038 CodeMirror.registerHelper = function(type, name, value) {
10039 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10040 helpers[type][name] = value;
10041 };
10042 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10043 CodeMirror.registerHelper(type, name, value);
10044 helpers[type]._global.push({pred: predicate, val: value});
10045 };
10046 };
10047
10048 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10049 // right), unit can be "char", "column" (like char, but doesn't
10050 // cross line boundaries), "word" (across next word), or "group" (to
10051 // the start of next group of word or non-word-non-whitespace
10052 // chars). The visually param controls whether, in right-to-left
10053 // text, direction 1 means to move towards the next index in the
10054 // string, or towards the character to the right of the current
10055 // position. The resulting position will have a hitSide=true
10056 // property if it reached the end of the document.
10057 function findPosH(doc, pos, dir, unit, visually) {
10058 var oldPos = pos;
10059 var origDir = dir;
10060 var lineObj = getLine(doc, pos.line);
10061 function findNextLine() {
10062 var l = pos.line + dir;
10063 if (l < doc.first || l >= doc.first + doc.size) { return false }
10064 pos = new Pos(l, pos.ch, pos.sticky);
10065 return lineObj = getLine(doc, l)
10066 }
10067 function moveOnce(boundToLine) {
10068 var next;
10069 if (visually) {
10070 next = moveVisually(doc.cm, lineObj, pos, dir);
10071 } else {
10072 next = moveLogically(lineObj, pos, dir);
10073 }
10074 if (next == null) {
10075 if (!boundToLine && findNextLine())
10076 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
10077 else
10078 { return false }
10079 } else {
10080 pos = next;
10081 }
10082 return true
10083 }
10084
10085 if (unit == "char") {
10086 moveOnce();
10087 } else if (unit == "column") {
10088 moveOnce(true);
10089 } else if (unit == "word" || unit == "group") {
10090 var sawType = null, group = unit == "group";
10091 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10092 for (var first = true;; first = false) {
10093 if (dir < 0 && !moveOnce(!first)) { break }
10094 var cur = lineObj.text.charAt(pos.ch) || "\n";
10095 var type = isWordChar(cur, helper) ? "w"
10096 : group && cur == "\n" ? "n"
10097 : !group || /\s/.test(cur) ? null
10098 : "p";
10099 if (group && !first && !type) { type = "s"; }
10100 if (sawType && sawType != type) {
10101 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10102 break
10103 }
10104
10105 if (type) { sawType = type; }
10106 if (dir > 0 && !moveOnce(!first)) { break }
10107 }
10108 }
10109 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10110 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10111 return result
10112 }
10113
10114 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10115 // "page" or "line". The resulting position will have a hitSide=true
10116 // property if it reached the end of the document.
10117 function findPosV(cm, pos, dir, unit) {
10118 var doc = cm.doc, x = pos.left, y;
10119 if (unit == "page") {
10120 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10121 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10122 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10123
10124 } else if (unit == "line") {
10125 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10126 }
10127 var target;
10128 for (;;) {
10129 target = coordsChar(cm, x, y);
10130 if (!target.outside) { break }
10131 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10132 y += dir * 5;
10133 }
10134 return target
10135 }
10136
10137 // CONTENTEDITABLE INPUT STYLE
10138
10139 var ContentEditableInput = function(cm) {
10140 this.cm = cm;
10141 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10142 this.polling = new Delayed();
10143 this.composing = null;
10144 this.gracePeriod = false;
10145 this.readDOMTimeout = null;
10146 };
10147
10148 ContentEditableInput.prototype.init = function (display) {
10149 var this$1 = this;
10150
10151 var input = this, cm = input.cm;
10152 var div = input.div = display.lineDiv;
10153 disableBrowserMagic(div, cm.options.spellcheck);
10154
10155 on(div, "paste", function (e) {
10156 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10157 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10158 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10159 });
10160
10161 on(div, "compositionstart", function (e) {
10162 this$1.composing = {data: e.data, done: false};
10163 });
10164 on(div, "compositionupdate", function (e) {
10165 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10166 });
10167 on(div, "compositionend", function (e) {
10168 if (this$1.composing) {
10169 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10170 this$1.composing.done = true;
10171 }
10172 });
10173
10174 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10175
10176 on(div, "input", function () {
10177 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10178 });
10179
10180 function onCopyCut(e) {
10181 if (signalDOMEvent(cm, e)) { return }
10182 if (cm.somethingSelected()) {
10183 setLastCopied({lineWise: false, text: cm.getSelections()});
10184 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10185 } else if (!cm.options.lineWiseCopyCut) {
10186 return
10187 } else {
10188 var ranges = copyableRanges(cm);
10189 setLastCopied({lineWise: true, text: ranges.text});
10190 if (e.type == "cut") {
10191 cm.operation(function () {
10192 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10193 cm.replaceSelection("", null, "cut");
10194 });
10195 }
10196 }
10197 if (e.clipboardData) {
10198 e.clipboardData.clearData();
10199 var content = lastCopied.text.join("\n");
10200 // iOS exposes the clipboard API, but seems to discard content inserted into it
10201 e.clipboardData.setData("Text", content);
10202 if (e.clipboardData.getData("Text") == content) {
10203 e.preventDefault();
10204 return
10205 }
10206 }
10207 // Old-fashioned briefly-focus-a-textarea hack
10208 var kludge = hiddenTextarea(), te = kludge.firstChild;
10209 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10210 te.value = lastCopied.text.join("\n");
10211 var hadFocus = document.activeElement;
10212 selectInput(te);
10213 setTimeout(function () {
10214 cm.display.lineSpace.removeChild(kludge);
10215 hadFocus.focus();
10216 if (hadFocus == div) { input.showPrimarySelection(); }
10217 }, 50);
10218 }
10219 on(div, "copy", onCopyCut);
10220 on(div, "cut", onCopyCut);
10221 };
10222
10223 ContentEditableInput.prototype.prepareSelection = function () {
10224 var result = prepareSelection(this.cm, false);
10225 result.focus = this.cm.state.focused;
10226 return result
10227 };
10228
10229 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10230 if (!info || !this.cm.display.view.length) { return }
10231 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10232 this.showMultipleSelections(info);
10233 };
10234
10235 ContentEditableInput.prototype.showPrimarySelection = function () {
10236 var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10237 var from = prim.from(), to = prim.to();
10238
10239 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10240 sel.removeAllRanges();
10241 return
10242 }
10243
10244 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10245 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10246 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10247 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10248 cmp(maxPos(curAnchor, curFocus), to) == 0)
10249 { return }
10250
10251 var view = cm.display.view;
10252 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10253 {node: view[0].measure.map[2], offset: 0};
10254 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10255 if (!end) {
10256 var measure = view[view.length - 1].measure;
10257 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10258 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10259 }
10260
10261 if (!start || !end) {
10262 sel.removeAllRanges();
10263 return
10264 }
10265
10266 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10267 try { rng = range(start.node, start.offset, end.offset, end.node); }
10268 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10269 if (rng) {
10270 if (!gecko && cm.state.focused) {
10271 sel.collapse(start.node, start.offset);
10272 if (!rng.collapsed) {
10273 sel.removeAllRanges();
10274 sel.addRange(rng);
10275 }
10276 } else {
10277 sel.removeAllRanges();
10278 sel.addRange(rng);
10279 }
10280 if (old && sel.anchorNode == null) { sel.addRange(old); }
10281 else if (gecko) { this.startGracePeriod(); }
10282 }
10283 this.rememberSelection();
10284 };
10285
10286 ContentEditableInput.prototype.startGracePeriod = function () {
10287 var this$1 = this;
10288
10289 clearTimeout(this.gracePeriod);
10290 this.gracePeriod = setTimeout(function () {
10291 this$1.gracePeriod = false;
10292 if (this$1.selectionChanged())
10293 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10294 }, 20);
10295 };
10296
10297 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10298 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10299 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10300 };
10301
10302 ContentEditableInput.prototype.rememberSelection = function () {
10303 var sel = window.getSelection();
10304 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10305 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10306 };
10307
10308 ContentEditableInput.prototype.selectionInEditor = function () {
10309 var sel = window.getSelection();
10310 if (!sel.rangeCount) { return false }
10311 var node = sel.getRangeAt(0).commonAncestorContainer;
10312 return contains(this.div, node)
10313 };
10314
10315 ContentEditableInput.prototype.focus = function () {
10316 if (this.cm.options.readOnly != "nocursor") {
10317 if (!this.selectionInEditor())
10318 { this.showSelection(this.prepareSelection(), true); }
10319 this.div.focus();
10320 }
10321 };
10322 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10323 ContentEditableInput.prototype.getField = function () { return this.div };
10324
10325 ContentEditableInput.prototype.supportsTouch = function () { return true };
10326
10327 ContentEditableInput.prototype.receivedFocus = function () {
10328 var input = this;
10329 if (this.selectionInEditor())
10330 { this.pollSelection(); }
10331 else
10332 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10333
10334 function poll() {
10335 if (input.cm.state.focused) {
10336 input.pollSelection();
10337 input.polling.set(input.cm.options.pollInterval, poll);
10338 }
10339 }
10340 this.polling.set(this.cm.options.pollInterval, poll);
10341 };
10342
10343 ContentEditableInput.prototype.selectionChanged = function () {
10344 var sel = window.getSelection();
10345 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10346 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10347 };
10348
10349 ContentEditableInput.prototype.pollSelection = function () {
10350 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10351 var sel = window.getSelection(), cm = this.cm;
10352 // On Android Chrome (version 56, at least), backspacing into an
10353 // uneditable block element will put the cursor in that element,
10354 // and then, because it's not editable, hide the virtual keyboard.
10355 // Because Android doesn't allow us to actually detect backspace
10356 // presses in a sane way, this code checks for when that happens
10357 // and simulates a backspace press in this case.
10358 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
10359 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10360 this.blur();
10361 this.focus();
10362 return
10363 }
10364 if (this.composing) { return }
10365 this.rememberSelection();
10366 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10367 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10368 if (anchor && head) { runInOp(cm, function () {
10369 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10370 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10371 }); }
10372 };
10373
10374 ContentEditableInput.prototype.pollContent = function () {
10375 if (this.readDOMTimeout != null) {
10376 clearTimeout(this.readDOMTimeout);
10377 this.readDOMTimeout = null;
10378 }
10379
10380 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10381 var from = sel.from(), to = sel.to();
10382 if (from.ch == 0 && from.line > cm.firstLine())
10383 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10384 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10385 { to = Pos(to.line + 1, 0); }
10386 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10387
10388 var fromIndex, fromLine, fromNode;
10389 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10390 fromLine = lineNo(display.view[0].line);
10391 fromNode = display.view[0].node;
10392 } else {
10393 fromLine = lineNo(display.view[fromIndex].line);
10394 fromNode = display.view[fromIndex - 1].node.nextSibling;
10395 }
10396 var toIndex = findViewIndex(cm, to.line);
10397 var toLine, toNode;
10398 if (toIndex == display.view.length - 1) {
10399 toLine = display.viewTo - 1;
10400 toNode = display.lineDiv.lastChild;
10401 } else {
10402 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10403 toNode = display.view[toIndex + 1].node.previousSibling;
10404 }
10405
10406 if (!fromNode) { return false }
10407 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10408 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10409 while (newText.length > 1 && oldText.length > 1) {
10410 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10411 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10412 else { break }
10413 }
10414
10415 var cutFront = 0, cutEnd = 0;
10416 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10417 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10418 { ++cutFront; }
10419 var newBot = lst(newText), oldBot = lst(oldText);
10420 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10421 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10422 while (cutEnd < maxCutEnd &&
10423 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10424 { ++cutEnd; }
10425 // Try to move start of change to start of selection if ambiguous
10426 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10427 while (cutFront && cutFront > from.ch &&
10428 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10429 cutFront--;
10430 cutEnd++;
10431 }
10432 }
10433
10434 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10435 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10436
10437 var chFrom = Pos(fromLine, cutFront);
10438 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10439 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10440 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10441 return true
10442 }
10443 };
10444
10445 ContentEditableInput.prototype.ensurePolled = function () {
10446 this.forceCompositionEnd();
10447 };
10448 ContentEditableInput.prototype.reset = function () {
10449 this.forceCompositionEnd();
10450 };
10451 ContentEditableInput.prototype.forceCompositionEnd = function () {
10452 if (!this.composing) { return }
10453 clearTimeout(this.readDOMTimeout);
10454 this.composing = null;
10455 this.updateFromDOM();
10456 this.div.blur();
10457 this.div.focus();
10458 };
10459 ContentEditableInput.prototype.readFromDOMSoon = function () {
10460 var this$1 = this;
10461
10462 if (this.readDOMTimeout != null) { return }
10463 this.readDOMTimeout = setTimeout(function () {
10464 this$1.readDOMTimeout = null;
10465 if (this$1.composing) {
10466 if (this$1.composing.done) { this$1.composing = null; }
10467 else { return }
10468 }
10469 this$1.updateFromDOM();
10470 }, 80);
10471 };
10472
10473 ContentEditableInput.prototype.updateFromDOM = function () {
10474 var this$1 = this;
10475
10476 if (this.cm.isReadOnly() || !this.pollContent())
10477 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10478 };
10479
10480 ContentEditableInput.prototype.setUneditable = function (node) {
10481 node.contentEditable = "false";
10482 };
10483
10484 ContentEditableInput.prototype.onKeyPress = function (e) {
10485 if (e.charCode == 0) { return }
10486 e.preventDefault();
10487 if (!this.cm.isReadOnly())
10488 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10489 };
10490
10491 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10492 this.div.contentEditable = String(val != "nocursor");
10493 };
10494
10495 ContentEditableInput.prototype.onContextMenu = function () {};
10496 ContentEditableInput.prototype.resetPosition = function () {};
10497
10498 ContentEditableInput.prototype.needsContentAttribute = true;
10499
10500 function posToDOM(cm, pos) {
10501 var view = findViewForLine(cm, pos.line);
10502 if (!view || view.hidden) { return null }
10503 var line = getLine(cm.doc, pos.line);
10504 var info = mapFromLineView(view, line, pos.line);
10505
10506 var order = getOrder(line, cm.doc.direction), side = "left";
10507 if (order) {
10508 var partPos = getBidiPartAt(order, pos.ch);
10509 side = partPos % 2 ? "right" : "left";
10510 }
10511 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10512 result.offset = result.collapse == "right" ? result.end : result.start;
10513 return result
10514 }
10515
10516 function isInGutter(node) {
10517 for (var scan = node; scan; scan = scan.parentNode)
10518 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10519 return false
10520 }
10521
10522 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10523
10524 function domTextBetween(cm, from, to, fromLine, toLine) {
10525 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
10526 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10527 function close() {
10528 if (closing) {
10529 text += lineSep;
10530 closing = false;
10531 }
10532 }
10533 function addText(str) {
10534 if (str) {
10535 close();
10536 text += str;
10537 }
10538 }
10539 function walk(node) {
10540 if (node.nodeType == 1) {
10541 var cmText = node.getAttribute("cm-text");
10542 if (cmText != null) {
10543 addText(cmText || node.textContent.replace(/\u200b/g, ""));
10544 return
10545 }
10546 var markerID = node.getAttribute("cm-marker"), range$$1;
10547 if (markerID) {
10548 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10549 if (found.length && (range$$1 = found[0].find(0)))
10550 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10551 return
10552 }
10553 if (node.getAttribute("contenteditable") == "false") { return }
10554 var isBlock = /^(pre|div|p)$/i.test(node.nodeName);
10555 if (isBlock) { close(); }
10556 for (var i = 0; i < node.childNodes.length; i++)
10557 { walk(node.childNodes[i]); }
10558 if (isBlock) { closing = true; }
10559 } else if (node.nodeType == 3) {
10560 addText(node.nodeValue);
10561 }
10562 }
10563 for (;;) {
10564 walk(from);
10565 if (from == to) { break }
10566 from = from.nextSibling;
10567 }
10568 return text
10569 }
10570
10571 function domToPos(cm, node, offset) {
10572 var lineNode;
10573 if (node == cm.display.lineDiv) {
10574 lineNode = cm.display.lineDiv.childNodes[offset];
10575 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10576 node = null; offset = 0;
10577 } else {
10578 for (lineNode = node;; lineNode = lineNode.parentNode) {
10579 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10580 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10581 }
10582 }
10583 for (var i = 0; i < cm.display.view.length; i++) {
10584 var lineView = cm.display.view[i];
10585 if (lineView.node == lineNode)
10586 { return locateNodeInLineView(lineView, node, offset) }
10587 }
10588 }
10589
10590 function locateNodeInLineView(lineView, node, offset) {
10591 var wrapper = lineView.text.firstChild, bad = false;
10592 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10593 if (node == wrapper) {
10594 bad = true;
10595 node = wrapper.childNodes[offset];
10596 offset = 0;
10597 if (!node) {
10598 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10599 return badPos(Pos(lineNo(line), line.text.length), bad)
10600 }
10601 }
10602
10603 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10604 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10605 textNode = node.firstChild;
10606 if (offset) { offset = textNode.nodeValue.length; }
10607 }
10608 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10609 var measure = lineView.measure, maps = measure.maps;
10610
10611 function find(textNode, topNode, offset) {
10612 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10613 var map$$1 = i < 0 ? measure.map : maps[i];
10614 for (var j = 0; j < map$$1.length; j += 3) {
10615 var curNode = map$$1[j + 2];
10616 if (curNode == textNode || curNode == topNode) {
10617 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10618 var ch = map$$1[j] + offset;
10619 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10620 return Pos(line, ch)
10621 }
10622 }
10623 }
10624 }
10625 var found = find(textNode, topNode, offset);
10626 if (found) { return badPos(found, bad) }
10627
10628 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10629 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10630 found = find(after, after.firstChild, 0);
10631 if (found)
10632 { return badPos(Pos(found.line, found.ch - dist), bad) }
10633 else
10634 { dist += after.textContent.length; }
10635 }
10636 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10637 found = find(before, before.firstChild, -1);
10638 if (found)
10639 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10640 else
10641 { dist$1 += before.textContent.length; }
10642 }
10643 }
10644
10645 // TEXTAREA INPUT STYLE
10646
10647 var TextareaInput = function(cm) {
10648 this.cm = cm;
10649 // See input.poll and input.reset
10650 this.prevInput = "";
10651
10652 // Flag that indicates whether we expect input to appear real soon
10653 // now (after some event like 'keypress' or 'input') and are
10654 // polling intensively.
10655 this.pollingFast = false;
10656 // Self-resetting timeout for the poller
10657 this.polling = new Delayed();
10658 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10659 this.hasSelection = false;
10660 this.composing = null;
10661 };
10662
10663 TextareaInput.prototype.init = function (display) {
10664 var this$1 = this;
10665
10666 var input = this, cm = this.cm;
10667
10668 // Wraps and hides input textarea
10669 var div = this.wrapper = hiddenTextarea();
10670 // The semihidden textarea that is focused when the editor is
10671 // focused, and receives input.
10672 var te = this.textarea = div.firstChild;
10673 display.wrapper.insertBefore(div, display.wrapper.firstChild);
10674
10675 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10676 if (ios) { te.style.width = "0px"; }
10677
10678 on(te, "input", function () {
10679 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10680 input.poll();
10681 });
10682
10683 on(te, "paste", function (e) {
10684 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10685
10686 cm.state.pasteIncoming = true;
10687 input.fastPoll();
10688 });
10689
10690 function prepareCopyCut(e) {
10691 if (signalDOMEvent(cm, e)) { return }
10692 if (cm.somethingSelected()) {
10693 setLastCopied({lineWise: false, text: cm.getSelections()});
10694 } else if (!cm.options.lineWiseCopyCut) {
10695 return
10696 } else {
10697 var ranges = copyableRanges(cm);
10698 setLastCopied({lineWise: true, text: ranges.text});
10699 if (e.type == "cut") {
10700 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10701 } else {
10702 input.prevInput = "";
10703 te.value = ranges.text.join("\n");
10704 selectInput(te);
10705 }
10706 }
10707 if (e.type == "cut") { cm.state.cutIncoming = true; }
10708 }
10709 on(te, "cut", prepareCopyCut);
10710 on(te, "copy", prepareCopyCut);
10711
10712 on(display.scroller, "paste", function (e) {
10713 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10714 cm.state.pasteIncoming = true;
10715 input.focus();
10716 });
10717
10718 // Prevent normal selection in the editor (we handle our own)
10719 on(display.lineSpace, "selectstart", function (e) {
10720 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10721 });
10722
10723 on(te, "compositionstart", function () {
10724 var start = cm.getCursor("from");
10725 if (input.composing) { input.composing.range.clear(); }
10726 input.composing = {
10727 start: start,
10728 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10729 };
10730 });
10731 on(te, "compositionend", function () {
10732 if (input.composing) {
10733 input.poll();
10734 input.composing.range.clear();
10735 input.composing = null;
10736 }
10737 });
10738 };
10739
10740 TextareaInput.prototype.prepareSelection = function () {
10741 // Redraw the selection and/or cursor
10742 var cm = this.cm, display = cm.display, doc = cm.doc;
10743 var result = prepareSelection(cm);
10744
10745 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10746 if (cm.options.moveInputWithCursor) {
10747 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10748 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10749 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10750 headPos.top + lineOff.top - wrapOff.top));
10751 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10752 headPos.left + lineOff.left - wrapOff.left));
10753 }
10754
10755 return result
10756 };
10757
10758 TextareaInput.prototype.showSelection = function (drawn) {
10759 var cm = this.cm, display = cm.display;
10760 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10761 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10762 if (drawn.teTop != null) {
10763 this.wrapper.style.top = drawn.teTop + "px";
10764 this.wrapper.style.left = drawn.teLeft + "px";
10765 }
10766 };
10767
10768 // Reset the input to correspond to the selection (or to be empty,
10769 // when not typing and nothing is selected)
10770 TextareaInput.prototype.reset = function (typing) {
10771 if (this.contextMenuPending || this.composing) { return }
10772 var cm = this.cm;
10773 if (cm.somethingSelected()) {
10774 this.prevInput = "";
10775 var content = cm.getSelection();
10776 this.textarea.value = content;
10777 if (cm.state.focused) { selectInput(this.textarea); }
10778 if (ie && ie_version >= 9) { this.hasSelection = content; }
10779 } else if (!typing) {
10780 this.prevInput = this.textarea.value = "";
10781 if (ie && ie_version >= 9) { this.hasSelection = null; }
10782 }
10783 };
10784
10785 TextareaInput.prototype.getField = function () { return this.textarea };
10786
10787 TextareaInput.prototype.supportsTouch = function () { return false };
10788
10789 TextareaInput.prototype.focus = function () {
10790 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10791 try { this.textarea.focus(); }
10792 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
10793 }
10794 };
10795
10796 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
10797
10798 TextareaInput.prototype.resetPosition = function () {
10799 this.wrapper.style.top = this.wrapper.style.left = 0;
10800 };
10801
10802 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
10803
10804 // Poll for input changes, using the normal rate of polling. This
10805 // runs as long as the editor is focused.
10806 TextareaInput.prototype.slowPoll = function () {
10807 var this$1 = this;
10808
10809 if (this.pollingFast) { return }
10810 this.polling.set(this.cm.options.pollInterval, function () {
10811 this$1.poll();
10812 if (this$1.cm.state.focused) { this$1.slowPoll(); }
10813 });
10814 };
10815
10816 // When an event has just come in that is likely to add or change
10817 // something in the input textarea, we poll faster, to ensure that
10818 // the change appears on the screen quickly.
10819 TextareaInput.prototype.fastPoll = function () {
10820 var missed = false, input = this;
10821 input.pollingFast = true;
10822 function p() {
10823 var changed = input.poll();
10824 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
10825 else {input.pollingFast = false; input.slowPoll();}
10826 }
10827 input.polling.set(20, p);
10828 };
10829
10830 // Read input from the textarea, and update the document to match.
10831 // When something is selected, it is present in the textarea, and
10832 // selected (unless it is huge, in which case a placeholder is
10833 // used). When nothing is selected, the cursor sits after previously
10834 // seen text (can be empty), which is stored in prevInput (we must
10835 // not reset the textarea when typing, because that breaks IME).
10836 TextareaInput.prototype.poll = function () {
10837 var this$1 = this;
10838
10839 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
10840 // Since this is called a *lot*, try to bail out as cheaply as
10841 // possible when it is clear that nothing happened. hasSelection
10842 // will be the case when there is a lot of text in the textarea,
10843 // in which case reading its value would be expensive.
10844 if (this.contextMenuPending || !cm.state.focused ||
10845 (hasSelection(input) && !prevInput && !this.composing) ||
10846 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
10847 { return false }
10848
10849 var text = input.value;
10850 // If nothing changed, bail.
10851 if (text == prevInput && !cm.somethingSelected()) { return false }
10852 // Work around nonsensical selection resetting in IE9/10, and
10853 // inexplicable appearance of private area unicode characters on
10854 // some key combos in Mac (#2689).
10855 if (ie && ie_version >= 9 && this.hasSelection === text ||
10856 mac && /[\uf700-\uf7ff]/.test(text)) {
10857 cm.display.input.reset();
10858 return false
10859 }
10860
10861 if (cm.doc.sel == cm.display.selForContextMenu) {
10862 var first = text.charCodeAt(0);
10863 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
10864 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
10865 }
10866 // Find the part of the input that is actually new
10867 var same = 0, l = Math.min(prevInput.length, text.length);
10868 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
10869
10870 runInOp(cm, function () {
10871 applyTextInput(cm, text.slice(same), prevInput.length - same,
10872 null, this$1.composing ? "*compose" : null);
10873
10874 // Don't leave long text in the textarea, since it makes further polling slow
10875 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
10876 else { this$1.prevInput = text; }
10877
10878 if (this$1.composing) {
10879 this$1.composing.range.clear();
10880 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
10881 {className: "CodeMirror-composing"});
10882 }
10883 });
10884 return true
10885 };
10886
10887 TextareaInput.prototype.ensurePolled = function () {
10888 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
10889 };
10890
10891 TextareaInput.prototype.onKeyPress = function () {
10892 if (ie && ie_version >= 9) { this.hasSelection = null; }
10893 this.fastPoll();
10894 };
10895
10896 TextareaInput.prototype.onContextMenu = function (e) {
10897 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
10898 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
10899 if (!pos || presto) { return } // Opera is difficult.
10900
10901 // Reset the current text selection only if the click is done outside of the selection
10902 // and 'resetSelectionOnContextMenu' option is true.
10903 var reset = cm.options.resetSelectionOnContextMenu;
10904 if (reset && cm.doc.sel.contains(pos) == -1)
10905 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
10906
10907 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
10908 input.wrapper.style.cssText = "position: absolute";
10909 var wrapperBox = input.wrapper.getBoundingClientRect();
10910 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);";
10911 var oldScrollY;
10912 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
10913 display.input.focus();
10914 if (webkit) { window.scrollTo(null, oldScrollY); }
10915 display.input.reset();
10916 // Adds "Select all" to context menu in FF
10917 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
10918 input.contextMenuPending = true;
10919 display.selForContextMenu = cm.doc.sel;
10920 clearTimeout(display.detectingSelectAll);
10921
10922 // Select-all will be greyed out if there's nothing to select, so
10923 // this adds a zero-width space so that we can later check whether
10924 // it got selected.
10925 function prepareSelectAllHack() {
10926 if (te.selectionStart != null) {
10927 var selected = cm.somethingSelected();
10928 var extval = "\u200b" + (selected ? te.value : "");
10929 te.value = "\u21da"; // Used to catch context-menu undo
10930 te.value = extval;
10931 input.prevInput = selected ? "" : "\u200b";
10932 te.selectionStart = 1; te.selectionEnd = extval.length;
10933 // Re-set this, in case some other handler touched the
10934 // selection in the meantime.
10935 display.selForContextMenu = cm.doc.sel;
10936 }
10937 }
10938 function rehide() {
10939 input.contextMenuPending = false;
10940 input.wrapper.style.cssText = oldWrapperCSS;
10941 te.style.cssText = oldCSS;
10942 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
10943
10944 // Try to detect the user choosing select-all
10945 if (te.selectionStart != null) {
10946 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
10947 var i = 0, poll = function () {
10948 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
10949 te.selectionEnd > 0 && input.prevInput == "\u200b") {
10950 operation(cm, selectAll)(cm);
10951 } else if (i++ < 10) {
10952 display.detectingSelectAll = setTimeout(poll, 500);
10953 } else {
10954 display.selForContextMenu = null;
10955 display.input.reset();
10956 }
10957 };
10958 display.detectingSelectAll = setTimeout(poll, 200);
10959 }
10960 }
10961
10962 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
10963 if (captureRightClick) {
10964 e_stop(e);
10965 var mouseup = function () {
10966 off(window, "mouseup", mouseup);
10967 setTimeout(rehide, 20);
10968 };
10969 on(window, "mouseup", mouseup);
10970 } else {
10971 setTimeout(rehide, 50);
10972 }
10973 };
10974
10975 TextareaInput.prototype.readOnlyChanged = function (val) {
10976 if (!val) { this.reset(); }
10977 this.textarea.disabled = val == "nocursor";
10978 };
10979
10980 TextareaInput.prototype.setUneditable = function () {};
10981
10982 TextareaInput.prototype.needsContentAttribute = false;
10983
10984 function fromTextArea(textarea, options) {
10985 options = options ? copyObj(options) : {};
10986 options.value = textarea.value;
10987 if (!options.tabindex && textarea.tabIndex)
10988 { options.tabindex = textarea.tabIndex; }
10989 if (!options.placeholder && textarea.placeholder)
10990 { options.placeholder = textarea.placeholder; }
10991 // Set autofocus to true if this textarea is focused, or if it has
10992 // autofocus and no other element is focused.
10993 if (options.autofocus == null) {
10994 var hasFocus = activeElt();
10995 options.autofocus = hasFocus == textarea ||
10996 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
10997 }
10998
10999 function save() {textarea.value = cm.getValue();}
11000
11001 var realSubmit;
11002 if (textarea.form) {
11003 on(textarea.form, "submit", save);
11004 // Deplorable hack to make the submit method do the right thing.
11005 if (!options.leaveSubmitMethodAlone) {
11006 var form = textarea.form;
11007 realSubmit = form.submit;
11008 try {
11009 var wrappedSubmit = form.submit = function () {
11010 save();
11011 form.submit = realSubmit;
11012 form.submit();
11013 form.submit = wrappedSubmit;
11014 };
11015 } catch(e) {}
11016 }
11017 }
11018
11019 options.finishInit = function (cm) {
11020 cm.save = save;
11021 cm.getTextArea = function () { return textarea; };
11022 cm.toTextArea = function () {
11023 cm.toTextArea = isNaN; // Prevent this from being ran twice
11024 save();
11025 textarea.parentNode.removeChild(cm.getWrapperElement());
11026 textarea.style.display = "";
11027 if (textarea.form) {
11028 off(textarea.form, "submit", save);
11029 if (typeof textarea.form.submit == "function")
11030 { textarea.form.submit = realSubmit; }
11031 }
11032 };
11033 };
11034
11035 textarea.style.display = "none";
11036 var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11037 options);
11038 return cm
11039 }
11040
11041 function addLegacyProps(CodeMirror) {
11042 CodeMirror.off = off;
11043 CodeMirror.on = on;
11044 CodeMirror.wheelEventPixels = wheelEventPixels;
11045 CodeMirror.Doc = Doc;
11046 CodeMirror.splitLines = splitLinesAuto;
11047 CodeMirror.countColumn = countColumn;
11048 CodeMirror.findColumn = findColumn;
11049 CodeMirror.isWordChar = isWordCharBasic;
11050 CodeMirror.Pass = Pass;
11051 CodeMirror.signal = signal;
11052 CodeMirror.Line = Line;
11053 CodeMirror.changeEnd = changeEnd;
11054 CodeMirror.scrollbarModel = scrollbarModel;
11055 CodeMirror.Pos = Pos;
11056 CodeMirror.cmpPos = cmp;
11057 CodeMirror.modes = modes;
11058 CodeMirror.mimeModes = mimeModes;
11059 CodeMirror.resolveMode = resolveMode;
11060 CodeMirror.getMode = getMode;
11061 CodeMirror.modeExtensions = modeExtensions;
11062 CodeMirror.extendMode = extendMode;
11063 CodeMirror.copyState = copyState;
11064 CodeMirror.startState = startState;
11065 CodeMirror.innerMode = innerMode;
11066 CodeMirror.commands = commands;
11067 CodeMirror.keyMap = keyMap;
11068 CodeMirror.keyName = keyName;
11069 CodeMirror.isModifierKey = isModifierKey;
11070 CodeMirror.lookupKey = lookupKey;
11071 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11072 CodeMirror.StringStream = StringStream;
11073 CodeMirror.SharedTextMarker = SharedTextMarker;
11074 CodeMirror.TextMarker = TextMarker;
11075 CodeMirror.LineWidget = LineWidget;
11076 CodeMirror.e_preventDefault = e_preventDefault;
11077 CodeMirror.e_stopPropagation = e_stopPropagation;
11078 CodeMirror.e_stop = e_stop;
11079 CodeMirror.addClass = addClass;
11080 CodeMirror.contains = contains;
11081 CodeMirror.rmClass = rmClass;
11082 CodeMirror.keyNames = keyNames;
11083 }
11084
11085 // EDITOR CONSTRUCTOR
11086
11087 defineOptions(CodeMirror$1);
11088
11089 addEditorMethods(CodeMirror$1);
11090
11091 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11092 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11093 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11094 { CodeMirror$1.prototype[prop] = (function(method) {
11095 return function() {return method.apply(this.doc, arguments)}
11096 })(Doc.prototype[prop]); } }
11097
11098 eventMixin(Doc);
11099
11100 // INPUT HANDLING
11101
11102 CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11103
11104 // MODE DEFINITION AND QUERYING
11105
11106 // Extra arguments are stored as the mode's dependencies, which is
11107 // used by (legacy) mechanisms like loadmode.js to automatically
11108 // load a mode. (Preferred mechanism is the require/define calls.)
11109 CodeMirror$1.defineMode = function(name/*, mode, …*/) {
11110 if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; }
11111 defineMode.apply(this, arguments);
11112 };
11113
11114 CodeMirror$1.defineMIME = defineMIME;
11115
11116 // Minimal default mode.
11117 CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11118 CodeMirror$1.defineMIME("text/plain", "null");
11119
11120 // EXTENSIONS
11121
11122 CodeMirror$1.defineExtension = function (name, func) {
11123 CodeMirror$1.prototype[name] = func;
11124 };
11125 CodeMirror$1.defineDocExtension = function (name, func) {
11126 Doc.prototype[name] = func;
11127 };
11128
11129 CodeMirror$1.fromTextArea = fromTextArea;
11130
11131 addLegacyProps(CodeMirror$1);
11132
11133 CodeMirror$1.version = "5.34.0";
11134
11135 return CodeMirror$1;
11136
11137 })));
11138
11139 },{}],13:[function(require,module,exports){
11140 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11141 // Distributed under an MIT license: http://codemirror.net/LICENSE
11142
11143 (function(mod) {
11144 if (typeof exports == "object" && typeof module == "object") // CommonJS
11145 mod(require("../../lib/codemirror"));
11146 else if (typeof define == "function" && define.amd) // AMD
11147 define(["../../lib/codemirror"], mod);
11148 else // Plain browser env
11149 mod(CodeMirror);
11150 })(function(CodeMirror) {
11151 "use strict";
11152
11153 CodeMirror.defineMode("css", function(config, parserConfig) {
11154 var inline = parserConfig.inline
11155 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11156
11157 var indentUnit = config.indentUnit,
11158 tokenHooks = parserConfig.tokenHooks,
11159 documentTypes = parserConfig.documentTypes || {},
11160 mediaTypes = parserConfig.mediaTypes || {},
11161 mediaFeatures = parserConfig.mediaFeatures || {},
11162 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11163 propertyKeywords = parserConfig.propertyKeywords || {},
11164 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11165 fontProperties = parserConfig.fontProperties || {},
11166 counterDescriptors = parserConfig.counterDescriptors || {},
11167 colorKeywords = parserConfig.colorKeywords || {},
11168 valueKeywords = parserConfig.valueKeywords || {},
11169 allowNested = parserConfig.allowNested,
11170 lineComment = parserConfig.lineComment,
11171 supportsAtComponent = parserConfig.supportsAtComponent === true;
11172
11173 var type, override;
11174 function ret(style, tp) { type = tp; return style; }
11175
11176 // Tokenizers
11177
11178 function tokenBase(stream, state) {
11179 var ch = stream.next();
11180 if (tokenHooks[ch]) {
11181 var result = tokenHooks[ch](stream, state);
11182 if (result !== false) return result;
11183 }
11184 if (ch == "@") {
11185 stream.eatWhile(/[\w\\\-]/);
11186 return ret("def", stream.current());
11187 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11188 return ret(null, "compare");
11189 } else if (ch == "\"" || ch == "'") {
11190 state.tokenize = tokenString(ch);
11191 return state.tokenize(stream, state);
11192 } else if (ch == "#") {
11193 stream.eatWhile(/[\w\\\-]/);
11194 return ret("atom", "hash");
11195 } else if (ch == "!") {
11196 stream.match(/^\s*\w*/);
11197 return ret("keyword", "important");
11198 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11199 stream.eatWhile(/[\w.%]/);
11200 return ret("number", "unit");
11201 } else if (ch === "-") {
11202 if (/[\d.]/.test(stream.peek())) {
11203 stream.eatWhile(/[\w.%]/);
11204 return ret("number", "unit");
11205 } else if (stream.match(/^-[\w\\\-]+/)) {
11206 stream.eatWhile(/[\w\\\-]/);
11207 if (stream.match(/^\s*:/, false))
11208 return ret("variable-2", "variable-definition");
11209 return ret("variable-2", "variable");
11210 } else if (stream.match(/^\w+-/)) {
11211 return ret("meta", "meta");
11212 }
11213 } else if (/[,+>*\/]/.test(ch)) {
11214 return ret(null, "select-op");
11215 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11216 return ret("qualifier", "qualifier");
11217 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11218 return ret(null, ch);
11219 } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
11220 ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
11221 ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
11222 stream.backUp(1);
11223 state.tokenize = tokenParenthesized;
11224 return ret("property", "word");
11225 } else if (/[\w\\\-]/.test(ch)) {
11226 stream.eatWhile(/[\w\\\-]/);
11227 return ret("property", "word");
11228 } else {
11229 return ret(null, null);
11230 }
11231 }
11232
11233 function tokenString(quote) {
11234 return function(stream, state) {
11235 var escaped = false, ch;
11236 while ((ch = stream.next()) != null) {
11237 if (ch == quote && !escaped) {
11238 if (quote == ")") stream.backUp(1);
11239 break;
11240 }
11241 escaped = !escaped && ch == "\\";
11242 }
11243 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11244 return ret("string", "string");
11245 };
11246 }
11247
11248 function tokenParenthesized(stream, state) {
11249 stream.next(); // Must be '('
11250 if (!stream.match(/\s*[\"\')]/, false))
11251 state.tokenize = tokenString(")");
11252 else
11253 state.tokenize = null;
11254 return ret(null, "(");
11255 }
11256
11257 // Context management
11258
11259 function Context(type, indent, prev) {
11260 this.type = type;
11261 this.indent = indent;
11262 this.prev = prev;
11263 }
11264
11265 function pushContext(state, stream, type, indent) {
11266 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11267 return type;
11268 }
11269
11270 function popContext(state) {
11271 if (state.context.prev)
11272 state.context = state.context.prev;
11273 return state.context.type;
11274 }
11275
11276 function pass(type, stream, state) {
11277 return states[state.context.type](type, stream, state);
11278 }
11279 function popAndPass(type, stream, state, n) {
11280 for (var i = n || 1; i > 0; i--)
11281 state.context = state.context.prev;
11282 return pass(type, stream, state);
11283 }
11284
11285 // Parser
11286
11287 function wordAsValue(stream) {
11288 var word = stream.current().toLowerCase();
11289 if (valueKeywords.hasOwnProperty(word))
11290 override = "atom";
11291 else if (colorKeywords.hasOwnProperty(word))
11292 override = "keyword";
11293 else
11294 override = "variable";
11295 }
11296
11297 var states = {};
11298
11299 states.top = function(type, stream, state) {
11300 if (type == "{") {
11301 return pushContext(state, stream, "block");
11302 } else if (type == "}" && state.context.prev) {
11303 return popContext(state);
11304 } else if (supportsAtComponent && /@component/i.test(type)) {
11305 return pushContext(state, stream, "atComponentBlock");
11306 } else if (/^@(-moz-)?document$/i.test(type)) {
11307 return pushContext(state, stream, "documentTypes");
11308 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11309 return pushContext(state, stream, "atBlock");
11310 } else if (/^@(font-face|counter-style)/i.test(type)) {
11311 state.stateArg = type;
11312 return "restricted_atBlock_before";
11313 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11314 return "keyframes";
11315 } else if (type && type.charAt(0) == "@") {
11316 return pushContext(state, stream, "at");
11317 } else if (type == "hash") {
11318 override = "builtin";
11319 } else if (type == "word") {
11320 override = "tag";
11321 } else if (type == "variable-definition") {
11322 return "maybeprop";
11323 } else if (type == "interpolation") {
11324 return pushContext(state, stream, "interpolation");
11325 } else if (type == ":") {
11326 return "pseudo";
11327 } else if (allowNested && type == "(") {
11328 return pushContext(state, stream, "parens");
11329 }
11330 return state.context.type;
11331 };
11332
11333 states.block = function(type, stream, state) {
11334 if (type == "word") {
11335 var word = stream.current().toLowerCase();
11336 if (propertyKeywords.hasOwnProperty(word)) {
11337 override = "property";
11338 return "maybeprop";
11339 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11340 override = "string-2";
11341 return "maybeprop";
11342 } else if (allowNested) {
11343 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11344 return "block";
11345 } else {
11346 override += " error";
11347 return "maybeprop";
11348 }
11349 } else if (type == "meta") {
11350 return "block";
11351 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11352 override = "error";
11353 return "block";
11354 } else {
11355 return states.top(type, stream, state);
11356 }
11357 };
11358
11359 states.maybeprop = function(type, stream, state) {
11360 if (type == ":") return pushContext(state, stream, "prop");
11361 return pass(type, stream, state);
11362 };
11363
11364 states.prop = function(type, stream, state) {
11365 if (type == ";") return popContext(state);
11366 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11367 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11368 if (type == "(") return pushContext(state, stream, "parens");
11369
11370 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11371 override += " error";
11372 } else if (type == "word") {
11373 wordAsValue(stream);
11374 } else if (type == "interpolation") {
11375 return pushContext(state, stream, "interpolation");
11376 }
11377 return "prop";
11378 };
11379
11380 states.propBlock = function(type, _stream, state) {
11381 if (type == "}") return popContext(state);
11382 if (type == "word") { override = "property"; return "maybeprop"; }
11383 return state.context.type;
11384 };
11385
11386 states.parens = function(type, stream, state) {
11387 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11388 if (type == ")") return popContext(state);
11389 if (type == "(") return pushContext(state, stream, "parens");
11390 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11391 if (type == "word") wordAsValue(stream);
11392 return "parens";
11393 };
11394
11395 states.pseudo = function(type, stream, state) {
11396 if (type == "meta") return "pseudo";
11397
11398 if (type == "word") {
11399 override = "variable-3";
11400 return state.context.type;
11401 }
11402 return pass(type, stream, state);
11403 };
11404
11405 states.documentTypes = function(type, stream, state) {
11406 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11407 override = "tag";
11408 return state.context.type;
11409 } else {
11410 return states.atBlock(type, stream, state);
11411 }
11412 };
11413
11414 states.atBlock = function(type, stream, state) {
11415 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11416 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11417 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11418
11419 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11420
11421 if (type == "word") {
11422 var word = stream.current().toLowerCase();
11423 if (word == "only" || word == "not" || word == "and" || word == "or")
11424 override = "keyword";
11425 else if (mediaTypes.hasOwnProperty(word))
11426 override = "attribute";
11427 else if (mediaFeatures.hasOwnProperty(word))
11428 override = "property";
11429 else if (mediaValueKeywords.hasOwnProperty(word))
11430 override = "keyword";
11431 else if (propertyKeywords.hasOwnProperty(word))
11432 override = "property";
11433 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11434 override = "string-2";
11435 else if (valueKeywords.hasOwnProperty(word))
11436 override = "atom";
11437 else if (colorKeywords.hasOwnProperty(word))
11438 override = "keyword";
11439 else
11440 override = "error";
11441 }
11442 return state.context.type;
11443 };
11444
11445 states.atComponentBlock = function(type, stream, state) {
11446 if (type == "}")
11447 return popAndPass(type, stream, state);
11448 if (type == "{")
11449 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11450 if (type == "word")
11451 override = "error";
11452 return state.context.type;
11453 };
11454
11455 states.atBlock_parens = function(type, stream, state) {
11456 if (type == ")") return popContext(state);
11457 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11458 return states.atBlock(type, stream, state);
11459 };
11460
11461 states.restricted_atBlock_before = function(type, stream, state) {
11462 if (type == "{")
11463 return pushContext(state, stream, "restricted_atBlock");
11464 if (type == "word" && state.stateArg == "@counter-style") {
11465 override = "variable";
11466 return "restricted_atBlock_before";
11467 }
11468 return pass(type, stream, state);
11469 };
11470
11471 states.restricted_atBlock = function(type, stream, state) {
11472 if (type == "}") {
11473 state.stateArg = null;
11474 return popContext(state);
11475 }
11476 if (type == "word") {
11477 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11478 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11479 override = "error";
11480 else
11481 override = "property";
11482 return "maybeprop";
11483 }
11484 return "restricted_atBlock";
11485 };
11486
11487 states.keyframes = function(type, stream, state) {
11488 if (type == "word") { override = "variable"; return "keyframes"; }
11489 if (type == "{") return pushContext(state, stream, "top");
11490 return pass(type, stream, state);
11491 };
11492
11493 states.at = function(type, stream, state) {
11494 if (type == ";") return popContext(state);
11495 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11496 if (type == "word") override = "tag";
11497 else if (type == "hash") override = "builtin";
11498 return "at";
11499 };
11500
11501 states.interpolation = function(type, stream, state) {
11502 if (type == "}") return popContext(state);
11503 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11504 if (type == "word") override = "variable";
11505 else if (type != "variable" && type != "(" && type != ")") override = "error";
11506 return "interpolation";
11507 };
11508
11509 return {
11510 startState: function(base) {
11511 return {tokenize: null,
11512 state: inline ? "block" : "top",
11513 stateArg: null,
11514 context: new Context(inline ? "block" : "top", base || 0, null)};
11515 },
11516
11517 token: function(stream, state) {
11518 if (!state.tokenize && stream.eatSpace()) return null;
11519 var style = (state.tokenize || tokenBase)(stream, state);
11520 if (style && typeof style == "object") {
11521 type = style[1];
11522 style = style[0];
11523 }
11524 override = style;
11525 if (type != "comment")
11526 state.state = states[state.state](type, stream, state);
11527 return override;
11528 },
11529
11530 indent: function(state, textAfter) {
11531 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11532 var indent = cx.indent;
11533 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11534 if (cx.prev) {
11535 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11536 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11537 // Resume indentation from parent context.
11538 cx = cx.prev;
11539 indent = cx.indent;
11540 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11541 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11542 // Dedent relative to current context.
11543 indent = Math.max(0, cx.indent - indentUnit);
11544 }
11545 }
11546 return indent;
11547 },
11548
11549 electricChars: "}",
11550 blockCommentStart: "/*",
11551 blockCommentEnd: "*/",
11552 blockCommentContinue: " * ",
11553 lineComment: lineComment,
11554 fold: "brace"
11555 };
11556 });
11557
11558 function keySet(array) {
11559 var keys = {};
11560 for (var i = 0; i < array.length; ++i) {
11561 keys[array[i].toLowerCase()] = true;
11562 }
11563 return keys;
11564 }
11565
11566 var documentTypes_ = [
11567 "domain", "regexp", "url", "url-prefix"
11568 ], documentTypes = keySet(documentTypes_);
11569
11570 var mediaTypes_ = [
11571 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11572 "tty", "tv", "embossed"
11573 ], mediaTypes = keySet(mediaTypes_);
11574
11575 var mediaFeatures_ = [
11576 "width", "min-width", "max-width", "height", "min-height", "max-height",
11577 "device-width", "min-device-width", "max-device-width", "device-height",
11578 "min-device-height", "max-device-height", "aspect-ratio",
11579 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11580 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11581 "max-color", "color-index", "min-color-index", "max-color-index",
11582 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11583 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11584 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11585 "pointer", "any-pointer", "hover", "any-hover"
11586 ], mediaFeatures = keySet(mediaFeatures_);
11587
11588 var mediaValueKeywords_ = [
11589 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11590 "interlace", "progressive"
11591 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11592
11593 var propertyKeywords_ = [
11594 "align-content", "align-items", "align-self", "alignment-adjust",
11595 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11596 "animation-direction", "animation-duration", "animation-fill-mode",
11597 "animation-iteration-count", "animation-name", "animation-play-state",
11598 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11599 "background", "background-attachment", "background-blend-mode", "background-clip",
11600 "background-color", "background-image", "background-origin", "background-position",
11601 "background-repeat", "background-size", "baseline-shift", "binding",
11602 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11603 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11604 "border-bottom-left-radius", "border-bottom-right-radius",
11605 "border-bottom-style", "border-bottom-width", "border-collapse",
11606 "border-color", "border-image", "border-image-outset",
11607 "border-image-repeat", "border-image-slice", "border-image-source",
11608 "border-image-width", "border-left", "border-left-color",
11609 "border-left-style", "border-left-width", "border-radius", "border-right",
11610 "border-right-color", "border-right-style", "border-right-width",
11611 "border-spacing", "border-style", "border-top", "border-top-color",
11612 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11613 "border-top-width", "border-width", "bottom", "box-decoration-break",
11614 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11615 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11616 "column-fill", "column-gap", "column-rule", "column-rule-color",
11617 "column-rule-style", "column-rule-width", "column-span", "column-width",
11618 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11619 "cue-after", "cue-before", "cursor", "direction", "display",
11620 "dominant-baseline", "drop-initial-after-adjust",
11621 "drop-initial-after-align", "drop-initial-before-adjust",
11622 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11623 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11624 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11625 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11626 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11627 "font-stretch", "font-style", "font-synthesis", "font-variant",
11628 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11629 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11630 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11631 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11632 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11633 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11634 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11635 "icon", "image-orientation", "image-rendering", "image-resolution",
11636 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11637 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11638 "line-stacking-shift", "line-stacking-strategy", "list-style",
11639 "list-style-image", "list-style-position", "list-style-type", "margin",
11640 "margin-bottom", "margin-left", "margin-right", "margin-top",
11641 "marks", "marquee-direction", "marquee-loop",
11642 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11643 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
11644 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11645 "opacity", "order", "orphans", "outline",
11646 "outline-color", "outline-offset", "outline-style", "outline-width",
11647 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11648 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11649 "page", "page-break-after", "page-break-before", "page-break-inside",
11650 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11651 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11652 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11653 "region-break-before", "region-break-inside", "region-fragment",
11654 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11655 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11656 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11657 "shape-outside", "size", "speak", "speak-as", "speak-header",
11658 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11659 "tab-size", "table-layout", "target", "target-name", "target-new",
11660 "target-position", "text-align", "text-align-last", "text-decoration",
11661 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11662 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11663 "text-emphasis-position", "text-emphasis-style", "text-height",
11664 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11665 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11666 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11667 "transition", "transition-delay", "transition-duration",
11668 "transition-property", "transition-timing-function", "unicode-bidi",
11669 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11670 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11671 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11672 "word-spacing", "word-wrap", "z-index",
11673 // SVG-specific
11674 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11675 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11676 "color-interpolation", "color-interpolation-filters",
11677 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11678 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11679 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11680 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11681 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11682 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11683 ], propertyKeywords = keySet(propertyKeywords_);
11684
11685 var nonStandardPropertyKeywords_ = [
11686 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11687 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11688 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11689 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11690 "searchfield-results-decoration", "zoom"
11691 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11692
11693 var fontProperties_ = [
11694 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11695 "font-stretch", "font-weight", "font-style"
11696 ], fontProperties = keySet(fontProperties_);
11697
11698 var counterDescriptors_ = [
11699 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11700 "speak-as", "suffix", "symbols", "system"
11701 ], counterDescriptors = keySet(counterDescriptors_);
11702
11703 var colorKeywords_ = [
11704 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11705 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11706 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11707 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11708 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11709 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11710 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11711 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11712 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11713 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11714 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11715 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11716 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11717 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11718 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11719 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11720 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11721 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11722 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11723 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11724 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11725 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11726 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11727 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11728 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11729 "whitesmoke", "yellow", "yellowgreen"
11730 ], colorKeywords = keySet(colorKeywords_);
11731
11732 var valueKeywords_ = [
11733 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11734 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11735 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11736 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11737 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11738 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11739 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11740 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11741 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11742 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11743 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11744 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11745 "compact", "condensed", "contain", "content", "contents",
11746 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11747 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11748 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11749 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11750 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11751 "dot-dash", "dot-dot-dash",
11752 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11753 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11754 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11755 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11756 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11757 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11758 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11759 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11760 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11761 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11762 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11763 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11764 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11765 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11766 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11767 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11768 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11769 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11770 "katakana", "katakana-iroha", "keep-all", "khmer",
11771 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11772 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11773 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11774 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11775 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11776 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11777 "media-controls-background", "media-current-time-display",
11778 "media-fullscreen-button", "media-mute-button", "media-play-button",
11779 "media-return-to-realtime-button", "media-rewind-button",
11780 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11781 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11782 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11783 "menu", "menulist", "menulist-button", "menulist-text",
11784 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11785 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11786 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11787 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11788 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11789 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11790 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11791 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11792 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11793 "progress", "push-button", "radial-gradient", "radio", "read-only",
11794 "read-write", "read-write-plaintext-only", "rectangle", "region",
11795 "relative", "repeat", "repeating-linear-gradient",
11796 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
11797 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
11798 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
11799 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
11800 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
11801 "searchfield-cancel-button", "searchfield-decoration",
11802 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
11803 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
11804 "simp-chinese-formal", "simp-chinese-informal", "single",
11805 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
11806 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
11807 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
11808 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
11809 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
11810 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
11811 "table-caption", "table-cell", "table-column", "table-column-group",
11812 "table-footer-group", "table-header-group", "table-row", "table-row-group",
11813 "tamil",
11814 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
11815 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
11816 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
11817 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
11818 "trad-chinese-formal", "trad-chinese-informal", "transform",
11819 "translate", "translate3d", "translateX", "translateY", "translateZ",
11820 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
11821 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
11822 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
11823 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
11824 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
11825 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
11826 "xx-large", "xx-small"
11827 ], valueKeywords = keySet(valueKeywords_);
11828
11829 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
11830 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
11831 .concat(valueKeywords_);
11832 CodeMirror.registerHelper("hintWords", "css", allWords);
11833
11834 function tokenCComment(stream, state) {
11835 var maybeEnd = false, ch;
11836 while ((ch = stream.next()) != null) {
11837 if (maybeEnd && ch == "/") {
11838 state.tokenize = null;
11839 break;
11840 }
11841 maybeEnd = (ch == "*");
11842 }
11843 return ["comment", "comment"];
11844 }
11845
11846 CodeMirror.defineMIME("text/css", {
11847 documentTypes: documentTypes,
11848 mediaTypes: mediaTypes,
11849 mediaFeatures: mediaFeatures,
11850 mediaValueKeywords: mediaValueKeywords,
11851 propertyKeywords: propertyKeywords,
11852 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11853 fontProperties: fontProperties,
11854 counterDescriptors: counterDescriptors,
11855 colorKeywords: colorKeywords,
11856 valueKeywords: valueKeywords,
11857 tokenHooks: {
11858 "/": function(stream, state) {
11859 if (!stream.eat("*")) return false;
11860 state.tokenize = tokenCComment;
11861 return tokenCComment(stream, state);
11862 }
11863 },
11864 name: "css"
11865 });
11866
11867 CodeMirror.defineMIME("text/x-scss", {
11868 mediaTypes: mediaTypes,
11869 mediaFeatures: mediaFeatures,
11870 mediaValueKeywords: mediaValueKeywords,
11871 propertyKeywords: propertyKeywords,
11872 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11873 colorKeywords: colorKeywords,
11874 valueKeywords: valueKeywords,
11875 fontProperties: fontProperties,
11876 allowNested: true,
11877 lineComment: "//",
11878 tokenHooks: {
11879 "/": function(stream, state) {
11880 if (stream.eat("/")) {
11881 stream.skipToEnd();
11882 return ["comment", "comment"];
11883 } else if (stream.eat("*")) {
11884 state.tokenize = tokenCComment;
11885 return tokenCComment(stream, state);
11886 } else {
11887 return ["operator", "operator"];
11888 }
11889 },
11890 ":": function(stream) {
11891 if (stream.match(/\s*\{/, false))
11892 return [null, null]
11893 return false;
11894 },
11895 "$": function(stream) {
11896 stream.match(/^[\w-]+/);
11897 if (stream.match(/^\s*:/, false))
11898 return ["variable-2", "variable-definition"];
11899 return ["variable-2", "variable"];
11900 },
11901 "#": function(stream) {
11902 if (!stream.eat("{")) return false;
11903 return [null, "interpolation"];
11904 }
11905 },
11906 name: "css",
11907 helperType: "scss"
11908 });
11909
11910 CodeMirror.defineMIME("text/x-less", {
11911 mediaTypes: mediaTypes,
11912 mediaFeatures: mediaFeatures,
11913 mediaValueKeywords: mediaValueKeywords,
11914 propertyKeywords: propertyKeywords,
11915 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11916 colorKeywords: colorKeywords,
11917 valueKeywords: valueKeywords,
11918 fontProperties: fontProperties,
11919 allowNested: true,
11920 lineComment: "//",
11921 tokenHooks: {
11922 "/": function(stream, state) {
11923 if (stream.eat("/")) {
11924 stream.skipToEnd();
11925 return ["comment", "comment"];
11926 } else if (stream.eat("*")) {
11927 state.tokenize = tokenCComment;
11928 return tokenCComment(stream, state);
11929 } else {
11930 return ["operator", "operator"];
11931 }
11932 },
11933 "@": function(stream) {
11934 if (stream.eat("{")) return [null, "interpolation"];
11935 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
11936 stream.eatWhile(/[\w\\\-]/);
11937 if (stream.match(/^\s*:/, false))
11938 return ["variable-2", "variable-definition"];
11939 return ["variable-2", "variable"];
11940 },
11941 "&": function() {
11942 return ["atom", "atom"];
11943 }
11944 },
11945 name: "css",
11946 helperType: "less"
11947 });
11948
11949 CodeMirror.defineMIME("text/x-gss", {
11950 documentTypes: documentTypes,
11951 mediaTypes: mediaTypes,
11952 mediaFeatures: mediaFeatures,
11953 propertyKeywords: propertyKeywords,
11954 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11955 fontProperties: fontProperties,
11956 counterDescriptors: counterDescriptors,
11957 colorKeywords: colorKeywords,
11958 valueKeywords: valueKeywords,
11959 supportsAtComponent: true,
11960 tokenHooks: {
11961 "/": function(stream, state) {
11962 if (!stream.eat("*")) return false;
11963 state.tokenize = tokenCComment;
11964 return tokenCComment(stream, state);
11965 }
11966 },
11967 name: "css",
11968 helperType: "gss"
11969 });
11970
11971 });
11972
11973 },{"../../lib/codemirror":12}],14:[function(require,module,exports){
11974 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11975 // Distributed under an MIT license: http://codemirror.net/LICENSE
11976
11977 (function(mod) {
11978 if (typeof exports == "object" && typeof module == "object") // CommonJS
11979 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
11980 else if (typeof define == "function" && define.amd) // AMD
11981 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
11982 else // Plain browser env
11983 mod(CodeMirror);
11984 })(function(CodeMirror) {
11985 "use strict";
11986
11987 var defaultTags = {
11988 script: [
11989 ["lang", /(javascript|babel)/i, "javascript"],
11990 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
11991 ["type", /./, "text/plain"],
11992 [null, null, "javascript"]
11993 ],
11994 style: [
11995 ["lang", /^css$/i, "css"],
11996 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
11997 ["type", /./, "text/plain"],
11998 [null, null, "css"]
11999 ]
12000 };
12001
12002 function maybeBackup(stream, pat, style) {
12003 var cur = stream.current(), close = cur.search(pat);
12004 if (close > -1) {
12005 stream.backUp(cur.length - close);
12006 } else if (cur.match(/<\/?$/)) {
12007 stream.backUp(cur.length);
12008 if (!stream.match(pat, false)) stream.match(cur);
12009 }
12010 return style;
12011 }
12012
12013 var attrRegexpCache = {};
12014 function getAttrRegexp(attr) {
12015 var regexp = attrRegexpCache[attr];
12016 if (regexp) return regexp;
12017 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12018 }
12019
12020 function getAttrValue(text, attr) {
12021 var match = text.match(getAttrRegexp(attr))
12022 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12023 }
12024
12025 function getTagRegexp(tagName, anchored) {
12026 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12027 }
12028
12029 function addTags(from, to) {
12030 for (var tag in from) {
12031 var dest = to[tag] || (to[tag] = []);
12032 var source = from[tag];
12033 for (var i = source.length - 1; i >= 0; i--)
12034 dest.unshift(source[i])
12035 }
12036 }
12037
12038 function findMatchingMode(tagInfo, tagText) {
12039 for (var i = 0; i < tagInfo.length; i++) {
12040 var spec = tagInfo[i];
12041 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12042 }
12043 }
12044
12045 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12046 var htmlMode = CodeMirror.getMode(config, {
12047 name: "xml",
12048 htmlMode: true,
12049 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12050 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12051 });
12052
12053 var tags = {};
12054 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12055 addTags(defaultTags, tags);
12056 if (configTags) addTags(configTags, tags);
12057 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12058 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12059
12060 function html(stream, state) {
12061 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12062 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12063 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12064 tags.hasOwnProperty(tagName)) {
12065 state.inTag = tagName + " "
12066 } else if (state.inTag && tag && />$/.test(stream.current())) {
12067 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12068 state.inTag = null
12069 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12070 var mode = CodeMirror.getMode(config, modeSpec)
12071 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12072 state.token = function (stream, state) {
12073 if (stream.match(endTagA, false)) {
12074 state.token = html;
12075 state.localState = state.localMode = null;
12076 return null;
12077 }
12078 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12079 };
12080 state.localMode = mode;
12081 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
12082 } else if (state.inTag) {
12083 state.inTag += stream.current()
12084 if (stream.eol()) state.inTag += " "
12085 }
12086 return style;
12087 };
12088
12089 return {
12090 startState: function () {
12091 var state = CodeMirror.startState(htmlMode);
12092 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12093 },
12094
12095 copyState: function (state) {
12096 var local;
12097 if (state.localState) {
12098 local = CodeMirror.copyState(state.localMode, state.localState);
12099 }
12100 return {token: state.token, inTag: state.inTag,
12101 localMode: state.localMode, localState: local,
12102 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12103 },
12104
12105 token: function (stream, state) {
12106 return state.token(stream, state);
12107 },
12108
12109 indent: function (state, textAfter, line) {
12110 if (!state.localMode || /^\s*<\//.test(textAfter))
12111 return htmlMode.indent(state.htmlState, textAfter);
12112 else if (state.localMode.indent)
12113 return state.localMode.indent(state.localState, textAfter, line);
12114 else
12115 return CodeMirror.Pass;
12116 },
12117
12118 innerMode: function (state) {
12119 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12120 }
12121 };
12122 }, "xml", "javascript", "css");
12123
12124 CodeMirror.defineMIME("text/html", "htmlmixed");
12125 });
12126
12127 },{"../../lib/codemirror":12,"../css/css":13,"../javascript/javascript":15,"../xml/xml":16}],15:[function(require,module,exports){
12128 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12129 // Distributed under an MIT license: http://codemirror.net/LICENSE
12130
12131 (function(mod) {
12132 if (typeof exports == "object" && typeof module == "object") // CommonJS
12133 mod(require("../../lib/codemirror"));
12134 else if (typeof define == "function" && define.amd) // AMD
12135 define(["../../lib/codemirror"], mod);
12136 else // Plain browser env
12137 mod(CodeMirror);
12138 })(function(CodeMirror) {
12139 "use strict";
12140
12141 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12142 var indentUnit = config.indentUnit;
12143 var statementIndent = parserConfig.statementIndent;
12144 var jsonldMode = parserConfig.jsonld;
12145 var jsonMode = parserConfig.json || jsonldMode;
12146 var isTS = parserConfig.typescript;
12147 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12148
12149 // Tokenizer
12150
12151 var keywords = function(){
12152 function kw(type) {return {type: type, style: "keyword"};}
12153 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12154 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12155
12156 return {
12157 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12158 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12159 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12160 "function": kw("function"), "catch": kw("catch"),
12161 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12162 "in": operator, "typeof": operator, "instanceof": operator,
12163 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12164 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12165 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12166 "await": C
12167 };
12168 }();
12169
12170 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12171 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12172
12173 function readRegexp(stream) {
12174 var escaped = false, next, inSet = false;
12175 while ((next = stream.next()) != null) {
12176 if (!escaped) {
12177 if (next == "/" && !inSet) return;
12178 if (next == "[") inSet = true;
12179 else if (inSet && next == "]") inSet = false;
12180 }
12181 escaped = !escaped && next == "\\";
12182 }
12183 }
12184
12185 // Used as scratch variables to communicate multiple values without
12186 // consing up tons of objects.
12187 var type, content;
12188 function ret(tp, style, cont) {
12189 type = tp; content = cont;
12190 return style;
12191 }
12192 function tokenBase(stream, state) {
12193 var ch = stream.next();
12194 if (ch == '"' || ch == "'") {
12195 state.tokenize = tokenString(ch);
12196 return state.tokenize(stream, state);
12197 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
12198 return ret("number", "number");
12199 } else if (ch == "." && stream.match("..")) {
12200 return ret("spread", "meta");
12201 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12202 return ret(ch);
12203 } else if (ch == "=" && stream.eat(">")) {
12204 return ret("=>", "operator");
12205 } else if (ch == "0" && stream.eat(/x/i)) {
12206 stream.eatWhile(/[\da-f]/i);
12207 return ret("number", "number");
12208 } else if (ch == "0" && stream.eat(/o/i)) {
12209 stream.eatWhile(/[0-7]/i);
12210 return ret("number", "number");
12211 } else if (ch == "0" && stream.eat(/b/i)) {
12212 stream.eatWhile(/[01]/i);
12213 return ret("number", "number");
12214 } else if (/\d/.test(ch)) {
12215 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
12216 return ret("number", "number");
12217 } else if (ch == "/") {
12218 if (stream.eat("*")) {
12219 state.tokenize = tokenComment;
12220 return tokenComment(stream, state);
12221 } else if (stream.eat("/")) {
12222 stream.skipToEnd();
12223 return ret("comment", "comment");
12224 } else if (expressionAllowed(stream, state, 1)) {
12225 readRegexp(stream);
12226 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
12227 return ret("regexp", "string-2");
12228 } else {
12229 stream.eat("=");
12230 return ret("operator", "operator", stream.current());
12231 }
12232 } else if (ch == "`") {
12233 state.tokenize = tokenQuasi;
12234 return tokenQuasi(stream, state);
12235 } else if (ch == "#") {
12236 stream.skipToEnd();
12237 return ret("error", "error");
12238 } else if (isOperatorChar.test(ch)) {
12239 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12240 if (stream.eat("=")) {
12241 if (ch == "!" || ch == "=") stream.eat("=")
12242 } else if (/[<>*+\-]/.test(ch)) {
12243 stream.eat(ch)
12244 if (ch == ">") stream.eat(ch)
12245 }
12246 }
12247 return ret("operator", "operator", stream.current());
12248 } else if (wordRE.test(ch)) {
12249 stream.eatWhile(wordRE);
12250 var word = stream.current()
12251 if (state.lastType != ".") {
12252 if (keywords.propertyIsEnumerable(word)) {
12253 var kw = keywords[word]
12254 return ret(kw.type, kw.style, word)
12255 }
12256 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false))
12257 return ret("async", "keyword", word)
12258 }
12259 return ret("variable", "variable", word)
12260 }
12261 }
12262
12263 function tokenString(quote) {
12264 return function(stream, state) {
12265 var escaped = false, next;
12266 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12267 state.tokenize = tokenBase;
12268 return ret("jsonld-keyword", "meta");
12269 }
12270 while ((next = stream.next()) != null) {
12271 if (next == quote && !escaped) break;
12272 escaped = !escaped && next == "\\";
12273 }
12274 if (!escaped) state.tokenize = tokenBase;
12275 return ret("string", "string");
12276 };
12277 }
12278
12279 function tokenComment(stream, state) {
12280 var maybeEnd = false, ch;
12281 while (ch = stream.next()) {
12282 if (ch == "/" && maybeEnd) {
12283 state.tokenize = tokenBase;
12284 break;
12285 }
12286 maybeEnd = (ch == "*");
12287 }
12288 return ret("comment", "comment");
12289 }
12290
12291 function tokenQuasi(stream, state) {
12292 var escaped = false, next;
12293 while ((next = stream.next()) != null) {
12294 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12295 state.tokenize = tokenBase;
12296 break;
12297 }
12298 escaped = !escaped && next == "\\";
12299 }
12300 return ret("quasi", "string-2", stream.current());
12301 }
12302
12303 var brackets = "([{}])";
12304 // This is a crude lookahead trick to try and notice that we're
12305 // parsing the argument patterns for a fat-arrow function before we
12306 // actually hit the arrow token. It only works if the arrow is on
12307 // the same line as the arguments and there's no strange noise
12308 // (comments) in between. Fallback is to only notice when we hit the
12309 // arrow, and not declare the arguments as locals for the arrow
12310 // body.
12311 function findFatArrow(stream, state) {
12312 if (state.fatArrowAt) state.fatArrowAt = null;
12313 var arrow = stream.string.indexOf("=>", stream.start);
12314 if (arrow < 0) return;
12315
12316 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12317 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12318 if (m) arrow = m.index
12319 }
12320
12321 var depth = 0, sawSomething = false;
12322 for (var pos = arrow - 1; pos >= 0; --pos) {
12323 var ch = stream.string.charAt(pos);
12324 var bracket = brackets.indexOf(ch);
12325 if (bracket >= 0 && bracket < 3) {
12326 if (!depth) { ++pos; break; }
12327 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12328 } else if (bracket >= 3 && bracket < 6) {
12329 ++depth;
12330 } else if (wordRE.test(ch)) {
12331 sawSomething = true;
12332 } else if (/["'\/]/.test(ch)) {
12333 return;
12334 } else if (sawSomething && !depth) {
12335 ++pos;
12336 break;
12337 }
12338 }
12339 if (sawSomething && !depth) state.fatArrowAt = pos;
12340 }
12341
12342 // Parser
12343
12344 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12345
12346 function JSLexical(indented, column, type, align, prev, info) {
12347 this.indented = indented;
12348 this.column = column;
12349 this.type = type;
12350 this.prev = prev;
12351 this.info = info;
12352 if (align != null) this.align = align;
12353 }
12354
12355 function inScope(state, varname) {
12356 for (var v = state.localVars; v; v = v.next)
12357 if (v.name == varname) return true;
12358 for (var cx = state.context; cx; cx = cx.prev) {
12359 for (var v = cx.vars; v; v = v.next)
12360 if (v.name == varname) return true;
12361 }
12362 }
12363
12364 function parseJS(state, style, type, content, stream) {
12365 var cc = state.cc;
12366 // Communicate our context to the combinators.
12367 // (Less wasteful than consing up a hundred closures on every call.)
12368 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12369
12370 if (!state.lexical.hasOwnProperty("align"))
12371 state.lexical.align = true;
12372
12373 while(true) {
12374 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12375 if (combinator(type, content)) {
12376 while(cc.length && cc[cc.length - 1].lex)
12377 cc.pop()();
12378 if (cx.marked) return cx.marked;
12379 if (type == "variable" && inScope(state, content)) return "variable-2";
12380 return style;
12381 }
12382 }
12383 }
12384
12385 // Combinator utils
12386
12387 var cx = {state: null, column: null, marked: null, cc: null};
12388 function pass() {
12389 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12390 }
12391 function cont() {
12392 pass.apply(null, arguments);
12393 return true;
12394 }
12395 function register(varname) {
12396 function inList(list) {
12397 for (var v = list; v; v = v.next)
12398 if (v.name == varname) return true;
12399 return false;
12400 }
12401 var state = cx.state;
12402 cx.marked = "def";
12403 if (state.context) {
12404 if (inList(state.localVars)) return;
12405 state.localVars = {name: varname, next: state.localVars};
12406 } else {
12407 if (inList(state.globalVars)) return;
12408 if (parserConfig.globalVars)
12409 state.globalVars = {name: varname, next: state.globalVars};
12410 }
12411 }
12412
12413 function isModifier(name) {
12414 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12415 }
12416
12417 // Combinators
12418
12419 var defaultVars = {name: "this", next: {name: "arguments"}};
12420 function pushcontext() {
12421 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
12422 cx.state.localVars = defaultVars;
12423 }
12424 function popcontext() {
12425 cx.state.localVars = cx.state.context.vars;
12426 cx.state.context = cx.state.context.prev;
12427 }
12428 function pushlex(type, info) {
12429 var result = function() {
12430 var state = cx.state, indent = state.indented;
12431 if (state.lexical.type == "stat") indent = state.lexical.indented;
12432 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12433 indent = outer.indented;
12434 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12435 };
12436 result.lex = true;
12437 return result;
12438 }
12439 function poplex() {
12440 var state = cx.state;
12441 if (state.lexical.prev) {
12442 if (state.lexical.type == ")")
12443 state.indented = state.lexical.indented;
12444 state.lexical = state.lexical.prev;
12445 }
12446 }
12447 poplex.lex = true;
12448
12449 function expect(wanted) {
12450 function exp(type) {
12451 if (type == wanted) return cont();
12452 else if (wanted == ";") return pass();
12453 else return cont(exp);
12454 };
12455 return exp;
12456 }
12457
12458 function statement(type, value) {
12459 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
12460 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12461 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12462 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12463 if (type == "debugger") return cont(expect(";"));
12464 if (type == "{") return cont(pushlex("}"), block, poplex);
12465 if (type == ";") return cont();
12466 if (type == "if") {
12467 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12468 cx.state.cc.pop()();
12469 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12470 }
12471 if (type == "function") return cont(functiondef);
12472 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12473 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
12474 if (type == "variable") {
12475 if (isTS && value == "declare") {
12476 cx.marked = "keyword"
12477 return cont(statement)
12478 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12479 cx.marked = "keyword"
12480 if (value == "enum") return cont(enumdef);
12481 else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
12482 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12483 } else if (isTS && value == "namespace") {
12484 cx.marked = "keyword"
12485 return cont(pushlex("form"), expression, block, poplex)
12486 } else {
12487 return cont(pushlex("stat"), maybelabel);
12488 }
12489 }
12490 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
12491 block, poplex, poplex);
12492 if (type == "case") return cont(expression, expect(":"));
12493 if (type == "default") return cont(expect(":"));
12494 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
12495 statement, poplex, popcontext);
12496 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12497 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12498 if (type == "async") return cont(statement)
12499 if (value == "@") return cont(expression, statement)
12500 return pass(pushlex("stat"), expression, expect(";"), poplex);
12501 }
12502 function expression(type, value) {
12503 return expressionInner(type, value, false);
12504 }
12505 function expressionNoComma(type, value) {
12506 return expressionInner(type, value, true);
12507 }
12508 function parenExpr(type) {
12509 if (type != "(") return pass()
12510 return cont(pushlex(")"), expression, expect(")"), poplex)
12511 }
12512 function expressionInner(type, value, noComma) {
12513 if (cx.state.fatArrowAt == cx.stream.start) {
12514 var body = noComma ? arrowBodyNoComma : arrowBody;
12515 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12516 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12517 }
12518
12519 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12520 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12521 if (type == "function") return cont(functiondef, maybeop);
12522 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12523 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12524 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12525 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12526 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12527 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12528 if (type == "quasi") return pass(quasi, maybeop);
12529 if (type == "new") return cont(maybeTarget(noComma));
12530 return cont();
12531 }
12532 function maybeexpression(type) {
12533 if (type.match(/[;\}\)\],]/)) return pass();
12534 return pass(expression);
12535 }
12536
12537 function maybeoperatorComma(type, value) {
12538 if (type == ",") return cont(expression);
12539 return maybeoperatorNoComma(type, value, false);
12540 }
12541 function maybeoperatorNoComma(type, value, noComma) {
12542 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12543 var expr = noComma == false ? expression : expressionNoComma;
12544 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12545 if (type == "operator") {
12546 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12547 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12548 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12549 if (value == "?") return cont(expression, expect(":"), expr);
12550 return cont(expr);
12551 }
12552 if (type == "quasi") { return pass(quasi, me); }
12553 if (type == ";") return;
12554 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12555 if (type == ".") return cont(property, me);
12556 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12557 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12558 if (type == "regexp") {
12559 cx.state.lastType = cx.marked = "operator"
12560 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12561 return cont(expr)
12562 }
12563 }
12564 function quasi(type, value) {
12565 if (type != "quasi") return pass();
12566 if (value.slice(value.length - 2) != "${") return cont(quasi);
12567 return cont(expression, continueQuasi);
12568 }
12569 function continueQuasi(type) {
12570 if (type == "}") {
12571 cx.marked = "string-2";
12572 cx.state.tokenize = tokenQuasi;
12573 return cont(quasi);
12574 }
12575 }
12576 function arrowBody(type) {
12577 findFatArrow(cx.stream, cx.state);
12578 return pass(type == "{" ? statement : expression);
12579 }
12580 function arrowBodyNoComma(type) {
12581 findFatArrow(cx.stream, cx.state);
12582 return pass(type == "{" ? statement : expressionNoComma);
12583 }
12584 function maybeTarget(noComma) {
12585 return function(type) {
12586 if (type == ".") return cont(noComma ? targetNoComma : target);
12587 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12588 else return pass(noComma ? expressionNoComma : expression);
12589 };
12590 }
12591 function target(_, value) {
12592 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12593 }
12594 function targetNoComma(_, value) {
12595 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12596 }
12597 function maybelabel(type) {
12598 if (type == ":") return cont(poplex, statement);
12599 return pass(maybeoperatorComma, expect(";"), poplex);
12600 }
12601 function property(type) {
12602 if (type == "variable") {cx.marked = "property"; return cont();}
12603 }
12604 function objprop(type, value) {
12605 if (type == "async") {
12606 cx.marked = "property";
12607 return cont(objprop);
12608 } else if (type == "variable" || cx.style == "keyword") {
12609 cx.marked = "property";
12610 if (value == "get" || value == "set") return cont(getterSetter);
12611 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12612 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12613 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12614 return cont(afterprop);
12615 } else if (type == "number" || type == "string") {
12616 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12617 return cont(afterprop);
12618 } else if (type == "jsonld-keyword") {
12619 return cont(afterprop);
12620 } else if (isTS && isModifier(value)) {
12621 cx.marked = "keyword"
12622 return cont(objprop)
12623 } else if (type == "[") {
12624 return cont(expression, maybetype, expect("]"), afterprop);
12625 } else if (type == "spread") {
12626 return cont(expressionNoComma, afterprop);
12627 } else if (value == "*") {
12628 cx.marked = "keyword";
12629 return cont(objprop);
12630 } else if (type == ":") {
12631 return pass(afterprop)
12632 }
12633 }
12634 function getterSetter(type) {
12635 if (type != "variable") return pass(afterprop);
12636 cx.marked = "property";
12637 return cont(functiondef);
12638 }
12639 function afterprop(type) {
12640 if (type == ":") return cont(expressionNoComma);
12641 if (type == "(") return pass(functiondef);
12642 }
12643 function commasep(what, end, sep) {
12644 function proceed(type, value) {
12645 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12646 var lex = cx.state.lexical;
12647 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12648 return cont(function(type, value) {
12649 if (type == end || value == end) return pass()
12650 return pass(what)
12651 }, proceed);
12652 }
12653 if (type == end || value == end) return cont();
12654 return cont(expect(end));
12655 }
12656 return function(type, value) {
12657 if (type == end || value == end) return cont();
12658 return pass(what, proceed);
12659 };
12660 }
12661 function contCommasep(what, end, info) {
12662 for (var i = 3; i < arguments.length; i++)
12663 cx.cc.push(arguments[i]);
12664 return cont(pushlex(end, info), commasep(what, end), poplex);
12665 }
12666 function block(type) {
12667 if (type == "}") return cont();
12668 return pass(statement, block);
12669 }
12670 function maybetype(type, value) {
12671 if (isTS) {
12672 if (type == ":") return cont(typeexpr);
12673 if (value == "?") return cont(maybetype);
12674 }
12675 }
12676 function mayberettype(type) {
12677 if (isTS && type == ":") {
12678 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12679 else return cont(typeexpr)
12680 }
12681 }
12682 function isKW(_, value) {
12683 if (value == "is") {
12684 cx.marked = "keyword"
12685 return cont()
12686 }
12687 }
12688 function typeexpr(type, value) {
12689 if (type == "variable" || value == "void") {
12690 if (value == "keyof") {
12691 cx.marked = "keyword"
12692 return cont(typeexpr)
12693 } else {
12694 cx.marked = "type"
12695 return cont(afterType)
12696 }
12697 }
12698 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12699 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12700 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12701 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
12702 }
12703 function maybeReturnType(type) {
12704 if (type == "=>") return cont(typeexpr)
12705 }
12706 function typeprop(type, value) {
12707 if (type == "variable" || cx.style == "keyword") {
12708 cx.marked = "property"
12709 return cont(typeprop)
12710 } else if (value == "?") {
12711 return cont(typeprop)
12712 } else if (type == ":") {
12713 return cont(typeexpr)
12714 } else if (type == "[") {
12715 return cont(expression, maybetype, expect("]"), typeprop)
12716 }
12717 }
12718 function typearg(type) {
12719 if (type == "variable") return cont(typearg)
12720 else if (type == ":") return cont(typeexpr)
12721 }
12722 function afterType(type, value) {
12723 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12724 if (value == "|" || type == ".") return cont(typeexpr)
12725 if (type == "[") return cont(expect("]"), afterType)
12726 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
12727 }
12728 function maybeTypeArgs(_, value) {
12729 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12730 }
12731 function typeparam() {
12732 return pass(typeexpr, maybeTypeDefault)
12733 }
12734 function maybeTypeDefault(_, value) {
12735 if (value == "=") return cont(typeexpr)
12736 }
12737 function vardef(_, value) {
12738 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
12739 return pass(pattern, maybetype, maybeAssign, vardefCont);
12740 }
12741 function pattern(type, value) {
12742 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
12743 if (type == "variable") { register(value); return cont(); }
12744 if (type == "spread") return cont(pattern);
12745 if (type == "[") return contCommasep(pattern, "]");
12746 if (type == "{") return contCommasep(proppattern, "}");
12747 }
12748 function proppattern(type, value) {
12749 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12750 register(value);
12751 return cont(maybeAssign);
12752 }
12753 if (type == "variable") cx.marked = "property";
12754 if (type == "spread") return cont(pattern);
12755 if (type == "}") return pass();
12756 return cont(expect(":"), pattern, maybeAssign);
12757 }
12758 function maybeAssign(_type, value) {
12759 if (value == "=") return cont(expressionNoComma);
12760 }
12761 function vardefCont(type) {
12762 if (type == ",") return cont(vardef);
12763 }
12764 function maybeelse(type, value) {
12765 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
12766 }
12767 function forspec(type) {
12768 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
12769 }
12770 function forspec1(type) {
12771 if (type == "var") return cont(vardef, expect(";"), forspec2);
12772 if (type == ";") return cont(forspec2);
12773 if (type == "variable") return cont(formaybeinof);
12774 return pass(expression, expect(";"), forspec2);
12775 }
12776 function formaybeinof(_type, value) {
12777 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12778 return cont(maybeoperatorComma, forspec2);
12779 }
12780 function forspec2(type, value) {
12781 if (type == ";") return cont(forspec3);
12782 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12783 return pass(expression, expect(";"), forspec3);
12784 }
12785 function forspec3(type) {
12786 if (type != ")") cont(expression);
12787 }
12788 function functiondef(type, value) {
12789 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
12790 if (type == "variable") {register(value); return cont(functiondef);}
12791 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
12792 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
12793 }
12794 function funarg(type, value) {
12795 if (value == "@") cont(expression, funarg)
12796 if (type == "spread") return cont(funarg);
12797 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
12798 return pass(pattern, maybetype, maybeAssign);
12799 }
12800 function classExpression(type, value) {
12801 // Class expressions may have an optional name.
12802 if (type == "variable") return className(type, value);
12803 return classNameAfter(type, value);
12804 }
12805 function className(type, value) {
12806 if (type == "variable") {register(value); return cont(classNameAfter);}
12807 }
12808 function classNameAfter(type, value) {
12809 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
12810 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
12811 if (value == "implements") cx.marked = "keyword";
12812 return cont(isTS ? typeexpr : expression, classNameAfter);
12813 }
12814 if (type == "{") return cont(pushlex("}"), classBody, poplex);
12815 }
12816 function classBody(type, value) {
12817 if (type == "async" ||
12818 (type == "variable" &&
12819 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
12820 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
12821 cx.marked = "keyword";
12822 return cont(classBody);
12823 }
12824 if (type == "variable" || cx.style == "keyword") {
12825 cx.marked = "property";
12826 return cont(isTS ? classfield : functiondef, classBody);
12827 }
12828 if (type == "[")
12829 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
12830 if (value == "*") {
12831 cx.marked = "keyword";
12832 return cont(classBody);
12833 }
12834 if (type == ";") return cont(classBody);
12835 if (type == "}") return cont();
12836 if (value == "@") return cont(expression, classBody)
12837 }
12838 function classfield(type, value) {
12839 if (value == "?") return cont(classfield)
12840 if (type == ":") return cont(typeexpr, maybeAssign)
12841 if (value == "=") return cont(expressionNoComma)
12842 return pass(functiondef)
12843 }
12844 function afterExport(type, value) {
12845 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
12846 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
12847 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
12848 return pass(statement);
12849 }
12850 function exportField(type, value) {
12851 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
12852 if (type == "variable") return pass(expressionNoComma, exportField);
12853 }
12854 function afterImport(type) {
12855 if (type == "string") return cont();
12856 return pass(importSpec, maybeMoreImports, maybeFrom);
12857 }
12858 function importSpec(type, value) {
12859 if (type == "{") return contCommasep(importSpec, "}");
12860 if (type == "variable") register(value);
12861 if (value == "*") cx.marked = "keyword";
12862 return cont(maybeAs);
12863 }
12864 function maybeMoreImports(type) {
12865 if (type == ",") return cont(importSpec, maybeMoreImports)
12866 }
12867 function maybeAs(_type, value) {
12868 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
12869 }
12870 function maybeFrom(_type, value) {
12871 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
12872 }
12873 function arrayLiteral(type) {
12874 if (type == "]") return cont();
12875 return pass(commasep(expressionNoComma, "]"));
12876 }
12877 function enumdef() {
12878 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
12879 }
12880 function enummember() {
12881 return pass(pattern, maybeAssign);
12882 }
12883
12884 function isContinuedStatement(state, textAfter) {
12885 return state.lastType == "operator" || state.lastType == "," ||
12886 isOperatorChar.test(textAfter.charAt(0)) ||
12887 /[,.]/.test(textAfter.charAt(0));
12888 }
12889
12890 function expressionAllowed(stream, state, backUp) {
12891 return state.tokenize == tokenBase &&
12892 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
12893 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
12894 }
12895
12896 // Interface
12897
12898 return {
12899 startState: function(basecolumn) {
12900 var state = {
12901 tokenize: tokenBase,
12902 lastType: "sof",
12903 cc: [],
12904 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
12905 localVars: parserConfig.localVars,
12906 context: parserConfig.localVars && {vars: parserConfig.localVars},
12907 indented: basecolumn || 0
12908 };
12909 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
12910 state.globalVars = parserConfig.globalVars;
12911 return state;
12912 },
12913
12914 token: function(stream, state) {
12915 if (stream.sol()) {
12916 if (!state.lexical.hasOwnProperty("align"))
12917 state.lexical.align = false;
12918 state.indented = stream.indentation();
12919 findFatArrow(stream, state);
12920 }
12921 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
12922 var style = state.tokenize(stream, state);
12923 if (type == "comment") return style;
12924 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
12925 return parseJS(state, style, type, content, stream);
12926 },
12927
12928 indent: function(state, textAfter) {
12929 if (state.tokenize == tokenComment) return CodeMirror.Pass;
12930 if (state.tokenize != tokenBase) return 0;
12931 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
12932 // Kludge to prevent 'maybelse' from blocking lexical scope pops
12933 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
12934 var c = state.cc[i];
12935 if (c == poplex) lexical = lexical.prev;
12936 else if (c != maybeelse) break;
12937 }
12938 while ((lexical.type == "stat" || lexical.type == "form") &&
12939 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
12940 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
12941 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
12942 lexical = lexical.prev;
12943 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
12944 lexical = lexical.prev;
12945 var type = lexical.type, closing = firstChar == type;
12946
12947 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
12948 else if (type == "form" && firstChar == "{") return lexical.indented;
12949 else if (type == "form") return lexical.indented + indentUnit;
12950 else if (type == "stat")
12951 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
12952 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
12953 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
12954 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
12955 else return lexical.indented + (closing ? 0 : indentUnit);
12956 },
12957
12958 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
12959 blockCommentStart: jsonMode ? null : "/*",
12960 blockCommentEnd: jsonMode ? null : "*/",
12961 blockCommentContinue: jsonMode ? null : " * ",
12962 lineComment: jsonMode ? null : "//",
12963 fold: "brace",
12964 closeBrackets: "()[]{}''\"\"``",
12965
12966 helperType: jsonMode ? "json" : "javascript",
12967 jsonldMode: jsonldMode,
12968 jsonMode: jsonMode,
12969
12970 expressionAllowed: expressionAllowed,
12971
12972 skipExpression: function(state) {
12973 var top = state.cc[state.cc.length - 1]
12974 if (top == expression || top == expressionNoComma) state.cc.pop()
12975 }
12976 };
12977 });
12978
12979 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
12980
12981 CodeMirror.defineMIME("text/javascript", "javascript");
12982 CodeMirror.defineMIME("text/ecmascript", "javascript");
12983 CodeMirror.defineMIME("application/javascript", "javascript");
12984 CodeMirror.defineMIME("application/x-javascript", "javascript");
12985 CodeMirror.defineMIME("application/ecmascript", "javascript");
12986 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
12987 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
12988 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
12989 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
12990 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
12991
12992 });
12993
12994 },{"../../lib/codemirror":12}],16:[function(require,module,exports){
12995 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12996 // Distributed under an MIT license: http://codemirror.net/LICENSE
12997
12998 (function(mod) {
12999 if (typeof exports == "object" && typeof module == "object") // CommonJS
13000 mod(require("../../lib/codemirror"));
13001 else if (typeof define == "function" && define.amd) // AMD
13002 define(["../../lib/codemirror"], mod);
13003 else // Plain browser env
13004 mod(CodeMirror);
13005 })(function(CodeMirror) {
13006 "use strict";
13007
13008 var htmlConfig = {
13009 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13010 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13011 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13012 'track': true, 'wbr': true, 'menuitem': true},
13013 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13014 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13015 'th': true, 'tr': true},
13016 contextGrabbers: {
13017 'dd': {'dd': true, 'dt': true},
13018 'dt': {'dd': true, 'dt': true},
13019 'li': {'li': true},
13020 'option': {'option': true, 'optgroup': true},
13021 'optgroup': {'optgroup': true},
13022 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13023 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13024 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13025 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13026 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13027 'rp': {'rp': true, 'rt': true},
13028 'rt': {'rp': true, 'rt': true},
13029 'tbody': {'tbody': true, 'tfoot': true},
13030 'td': {'td': true, 'th': true},
13031 'tfoot': {'tbody': true},
13032 'th': {'td': true, 'th': true},
13033 'thead': {'tbody': true, 'tfoot': true},
13034 'tr': {'tr': true}
13035 },
13036 doNotIndent: {"pre": true},
13037 allowUnquoted: true,
13038 allowMissing: true,
13039 caseFold: true
13040 }
13041
13042 var xmlConfig = {
13043 autoSelfClosers: {},
13044 implicitlyClosed: {},
13045 contextGrabbers: {},
13046 doNotIndent: {},
13047 allowUnquoted: false,
13048 allowMissing: false,
13049 allowMissingTagName: false,
13050 caseFold: false
13051 }
13052
13053 CodeMirror.defineMode("xml", function(editorConf, config_) {
13054 var indentUnit = editorConf.indentUnit
13055 var config = {}
13056 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13057 for (var prop in defaults) config[prop] = defaults[prop]
13058 for (var prop in config_) config[prop] = config_[prop]
13059
13060 // Return variables for tokenizers
13061 var type, setStyle;
13062
13063 function inText(stream, state) {
13064 function chain(parser) {
13065 state.tokenize = parser;
13066 return parser(stream, state);
13067 }
13068
13069 var ch = stream.next();
13070 if (ch == "<") {
13071 if (stream.eat("!")) {
13072 if (stream.eat("[")) {
13073 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13074 else return null;
13075 } else if (stream.match("--")) {
13076 return chain(inBlock("comment", "-->"));
13077 } else if (stream.match("DOCTYPE", true, true)) {
13078 stream.eatWhile(/[\w\._\-]/);
13079 return chain(doctype(1));
13080 } else {
13081 return null;
13082 }
13083 } else if (stream.eat("?")) {
13084 stream.eatWhile(/[\w\._\-]/);
13085 state.tokenize = inBlock("meta", "?>");
13086 return "meta";
13087 } else {
13088 type = stream.eat("/") ? "closeTag" : "openTag";
13089 state.tokenize = inTag;
13090 return "tag bracket";
13091 }
13092 } else if (ch == "&") {
13093 var ok;
13094 if (stream.eat("#")) {
13095 if (stream.eat("x")) {
13096 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13097 } else {
13098 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13099 }
13100 } else {
13101 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13102 }
13103 return ok ? "atom" : "error";
13104 } else {
13105 stream.eatWhile(/[^&<]/);
13106 return null;
13107 }
13108 }
13109 inText.isInText = true;
13110
13111 function inTag(stream, state) {
13112 var ch = stream.next();
13113 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13114 state.tokenize = inText;
13115 type = ch == ">" ? "endTag" : "selfcloseTag";
13116 return "tag bracket";
13117 } else if (ch == "=") {
13118 type = "equals";
13119 return null;
13120 } else if (ch == "<") {
13121 state.tokenize = inText;
13122 state.state = baseState;
13123 state.tagName = state.tagStart = null;
13124 var next = state.tokenize(stream, state);
13125 return next ? next + " tag error" : "tag error";
13126 } else if (/[\'\"]/.test(ch)) {
13127 state.tokenize = inAttribute(ch);
13128 state.stringStartCol = stream.column();
13129 return state.tokenize(stream, state);
13130 } else {
13131 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13132 return "word";
13133 }
13134 }
13135
13136 function inAttribute(quote) {
13137 var closure = function(stream, state) {
13138 while (!stream.eol()) {
13139 if (stream.next() == quote) {
13140 state.tokenize = inTag;
13141 break;
13142 }
13143 }
13144 return "string";
13145 };
13146 closure.isInAttribute = true;
13147 return closure;
13148 }
13149
13150 function inBlock(style, terminator) {
13151 return function(stream, state) {
13152 while (!stream.eol()) {
13153 if (stream.match(terminator)) {
13154 state.tokenize = inText;
13155 break;
13156 }
13157 stream.next();
13158 }
13159 return style;
13160 };
13161 }
13162 function doctype(depth) {
13163 return function(stream, state) {
13164 var ch;
13165 while ((ch = stream.next()) != null) {
13166 if (ch == "<") {
13167 state.tokenize = doctype(depth + 1);
13168 return state.tokenize(stream, state);
13169 } else if (ch == ">") {
13170 if (depth == 1) {
13171 state.tokenize = inText;
13172 break;
13173 } else {
13174 state.tokenize = doctype(depth - 1);
13175 return state.tokenize(stream, state);
13176 }
13177 }
13178 }
13179 return "meta";
13180 };
13181 }
13182
13183 function Context(state, tagName, startOfLine) {
13184 this.prev = state.context;
13185 this.tagName = tagName;
13186 this.indent = state.indented;
13187 this.startOfLine = startOfLine;
13188 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13189 this.noIndent = true;
13190 }
13191 function popContext(state) {
13192 if (state.context) state.context = state.context.prev;
13193 }
13194 function maybePopContext(state, nextTagName) {
13195 var parentTagName;
13196 while (true) {
13197 if (!state.context) {
13198 return;
13199 }
13200 parentTagName = state.context.tagName;
13201 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13202 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13203 return;
13204 }
13205 popContext(state);
13206 }
13207 }
13208
13209 function baseState(type, stream, state) {
13210 if (type == "openTag") {
13211 state.tagStart = stream.column();
13212 return tagNameState;
13213 } else if (type == "closeTag") {
13214 return closeTagNameState;
13215 } else {
13216 return baseState;
13217 }
13218 }
13219 function tagNameState(type, stream, state) {
13220 if (type == "word") {
13221 state.tagName = stream.current();
13222 setStyle = "tag";
13223 return attrState;
13224 } else if (config.allowMissingTagName && type == "endTag") {
13225 setStyle = "tag bracket";
13226 return attrState(type, stream, state);
13227 } else {
13228 setStyle = "error";
13229 return tagNameState;
13230 }
13231 }
13232 function closeTagNameState(type, stream, state) {
13233 if (type == "word") {
13234 var tagName = stream.current();
13235 if (state.context && state.context.tagName != tagName &&
13236 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13237 popContext(state);
13238 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13239 setStyle = "tag";
13240 return closeState;
13241 } else {
13242 setStyle = "tag error";
13243 return closeStateErr;
13244 }
13245 } else if (config.allowMissingTagName && type == "endTag") {
13246 setStyle = "tag bracket";
13247 return closeState(type, stream, state);
13248 } else {
13249 setStyle = "error";
13250 return closeStateErr;
13251 }
13252 }
13253
13254 function closeState(type, _stream, state) {
13255 if (type != "endTag") {
13256 setStyle = "error";
13257 return closeState;
13258 }
13259 popContext(state);
13260 return baseState;
13261 }
13262 function closeStateErr(type, stream, state) {
13263 setStyle = "error";
13264 return closeState(type, stream, state);
13265 }
13266
13267 function attrState(type, _stream, state) {
13268 if (type == "word") {
13269 setStyle = "attribute";
13270 return attrEqState;
13271 } else if (type == "endTag" || type == "selfcloseTag") {
13272 var tagName = state.tagName, tagStart = state.tagStart;
13273 state.tagName = state.tagStart = null;
13274 if (type == "selfcloseTag" ||
13275 config.autoSelfClosers.hasOwnProperty(tagName)) {
13276 maybePopContext(state, tagName);
13277 } else {
13278 maybePopContext(state, tagName);
13279 state.context = new Context(state, tagName, tagStart == state.indented);
13280 }
13281 return baseState;
13282 }
13283 setStyle = "error";
13284 return attrState;
13285 }
13286 function attrEqState(type, stream, state) {
13287 if (type == "equals") return attrValueState;
13288 if (!config.allowMissing) setStyle = "error";
13289 return attrState(type, stream, state);
13290 }
13291 function attrValueState(type, stream, state) {
13292 if (type == "string") return attrContinuedState;
13293 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13294 setStyle = "error";
13295 return attrState(type, stream, state);
13296 }
13297 function attrContinuedState(type, stream, state) {
13298 if (type == "string") return attrContinuedState;
13299 return attrState(type, stream, state);
13300 }
13301
13302 return {
13303 startState: function(baseIndent) {
13304 var state = {tokenize: inText,
13305 state: baseState,
13306 indented: baseIndent || 0,
13307 tagName: null, tagStart: null,
13308 context: null}
13309 if (baseIndent != null) state.baseIndent = baseIndent
13310 return state
13311 },
13312
13313 token: function(stream, state) {
13314 if (!state.tagName && stream.sol())
13315 state.indented = stream.indentation();
13316
13317 if (stream.eatSpace()) return null;
13318 type = null;
13319 var style = state.tokenize(stream, state);
13320 if ((style || type) && style != "comment") {
13321 setStyle = null;
13322 state.state = state.state(type || style, stream, state);
13323 if (setStyle)
13324 style = setStyle == "error" ? style + " error" : setStyle;
13325 }
13326 return style;
13327 },
13328
13329 indent: function(state, textAfter, fullLine) {
13330 var context = state.context;
13331 // Indent multi-line strings (e.g. css).
13332 if (state.tokenize.isInAttribute) {
13333 if (state.tagStart == state.indented)
13334 return state.stringStartCol + 1;
13335 else
13336 return state.indented + indentUnit;
13337 }
13338 if (context && context.noIndent) return CodeMirror.Pass;
13339 if (state.tokenize != inTag && state.tokenize != inText)
13340 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13341 // Indent the starts of attribute names.
13342 if (state.tagName) {
13343 if (config.multilineTagIndentPastTag !== false)
13344 return state.tagStart + state.tagName.length + 2;
13345 else
13346 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13347 }
13348 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13349 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13350 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13351 while (context) {
13352 if (context.tagName == tagAfter[2]) {
13353 context = context.prev;
13354 break;
13355 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13356 context = context.prev;
13357 } else {
13358 break;
13359 }
13360 }
13361 } else if (tagAfter) { // Opening tag spotted
13362 while (context) {
13363 var grabbers = config.contextGrabbers[context.tagName];
13364 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13365 context = context.prev;
13366 else
13367 break;
13368 }
13369 }
13370 while (context && context.prev && !context.startOfLine)
13371 context = context.prev;
13372 if (context) return context.indent + indentUnit;
13373 else return state.baseIndent || 0;
13374 },
13375
13376 electricInput: /<\/[\s\w:]+>$/,
13377 blockCommentStart: "<!--",
13378 blockCommentEnd: "-->",
13379
13380 configuration: config.htmlMode ? "html" : "xml",
13381 helperType: config.htmlMode ? "html" : "xml",
13382
13383 skipAttribute: function(state) {
13384 if (state.state == attrValueState)
13385 state.state = attrState
13386 }
13387 };
13388 });
13389
13390 CodeMirror.defineMIME("text/xml", "xml");
13391 CodeMirror.defineMIME("application/xml", "xml");
13392 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13393 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13394
13395 });
13396
13397 },{"../../lib/codemirror":12}],17:[function(require,module,exports){
13398 (function(global,factory){if(typeof define === 'function' && define.amd){define(['exports'],factory);}else if(typeof exports !== 'undefined'){factory(exports);}else {var mod={exports:{}};factory(mod.exports);global.decko = mod.exports;}})(this,function(exports){'use strict';exports.__esModule = true;var EMPTY={};var HOP=Object.prototype.hasOwnProperty;var fns={memoize:function memoize(fn){var opt=arguments.length <= 1 || arguments[1] === undefined?EMPTY:arguments[1];var cache=opt.cache || {};return function(){for(var _len=arguments.length,a=Array(_len),_key=0;_key < _len;_key++) {a[_key] = arguments[_key];}var k=String(a[0]);if(opt.caseSensitive === false)k = k.toLowerCase();return HOP.call(cache,k)?cache[k]:cache[k] = fn.apply(this,a);};},debounce:function debounce(fn,opts){if(typeof opts === 'function'){var p=fn;fn = opts;opts = p;}var delay=opts && opts.delay || opts || 0,args=undefined,context=undefined,timer=undefined;return function(){for(var _len2=arguments.length,a=Array(_len2),_key2=0;_key2 < _len2;_key2++) {a[_key2] = arguments[_key2];}args = a;context = this;if(!timer)timer = setTimeout(function(){fn.apply(context,args);args = context = timer = null;},delay);};},bind:function bind(target,key,_ref){var fn=_ref.value;return {configurable:true,get:function get(){var value=fn.bind(this);Object.defineProperty(this,key,{value:value,configurable:true,writable:true});return value;}};}};var memoize=multiMethod(fns.memoize),debounce=multiMethod(fns.debounce),bind=multiMethod(function(f,c){return f.bind(c);},function(){return fns.bind;});exports.memoize = memoize;exports.debounce = debounce;exports.bind = bind;exports['default'] = {memoize:memoize,debounce:debounce,bind:bind};function multiMethod(inner,deco){deco = deco || inner.decorate || decorator(inner);var d=deco();return function(){for(var _len3=arguments.length,args=Array(_len3),_key3=0;_key3 < _len3;_key3++) {args[_key3] = arguments[_key3];}var l=args.length;return (l < 2?deco:l > 2?d:inner).apply(undefined,args);};}function decorator(fn){return function(opt){return typeof opt === 'function'?fn(opt):function(target,key,desc){desc.value = fn(desc.value,opt,target,key,desc);};};}});
13399
13400
13401 },{}],18:[function(require,module,exports){
13402 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;
13403
13404
13405 },{}],19:[function(require,module,exports){
13406 (function (global, factory) {
13407 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13408 typeof define === 'function' && define.amd ? define(factory) :
13409 (global.preactRenderToString = factory());
13410 }(this, (function () {
13411
13412 var NON_DIMENSION_PROPS = {
13413 boxFlex: 1, boxFlexGroup: 1, columnCount: 1, fillOpacity: 1, flex: 1, flexGrow: 1,
13414 flexPositive: 1, flexShrink: 1, flexNegative: 1, fontWeight: 1, lineClamp: 1, lineHeight: 1,
13415 opacity: 1, order: 1, orphans: 1, strokeOpacity: 1, widows: 1, zIndex: 1, zoom: 1
13416 };
13417
13418 var ESC = {
13419 '<': '&lt;',
13420 '>': '&gt;',
13421 '"': '&quot;',
13422 '&': '&amp;'
13423 };
13424
13425 var objectKeys = Object.keys || function (obj) {
13426 var keys = [];
13427 for (var i in obj) {
13428 if (obj.hasOwnProperty(i)) keys.push(i);
13429 }return keys;
13430 };
13431
13432 var encodeEntities = function encodeEntities(s) {
13433 return String(s).replace(/[<>"&]/g, escapeChar);
13434 };
13435
13436 var escapeChar = function escapeChar(a) {
13437 return ESC[a] || a;
13438 };
13439
13440 var falsey = function falsey(v) {
13441 return v == null || v === false;
13442 };
13443
13444 var memoize = function memoize(fn) {
13445 var mem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13446 return function (v) {
13447 return mem[v] || (mem[v] = fn(v));
13448 };
13449 };
13450
13451 var indent = function indent(s, char) {
13452 return String(s).replace(/(\n+)/g, '$1' + (char || '\t'));
13453 };
13454
13455 var isLargeString = function isLargeString(s, length, ignoreLines) {
13456 return String(s).length > (length || 40) || !ignoreLines && String(s).indexOf('\n') !== -1 || String(s).indexOf('<') !== -1;
13457 };
13458
13459 function styleObjToCss(s) {
13460 var str = '';
13461 for (var prop in s) {
13462 var val = s[prop];
13463 if (val != null) {
13464 if (str) str += ' ';
13465 str += jsToCss(prop);
13466 str += ': ';
13467 str += val;
13468 if (typeof val === 'number' && !NON_DIMENSION_PROPS[prop]) {
13469 str += 'px';
13470 }
13471 str += ';';
13472 }
13473 }
13474 return str || undefined;
13475 }
13476
13477 function hashToClassName(c) {
13478 var str = '';
13479 for (var prop in c) {
13480 if (c[prop]) {
13481 if (str) str += ' ';
13482 str += prop;
13483 }
13484 }
13485 return str;
13486 }
13487
13488 var jsToCss = memoize(function (s) {
13489 return s.replace(/([A-Z])/g, '-$1').toLowerCase();
13490 });
13491
13492 function assign(obj, props) {
13493 for (var i in props) {
13494 obj[i] = props[i];
13495 }return obj;
13496 }
13497
13498 function getNodeProps(vnode) {
13499 var defaultProps = vnode.nodeName.defaultProps,
13500 props = assign({}, defaultProps || vnode.attributes);
13501 if (defaultProps) assign(props, vnode.attributes);
13502 if (vnode.children) props.children = vnode.children;
13503 return props;
13504 }
13505
13506 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
13507
13508 var SHALLOW = { shallow: true };
13509
13510 var UNNAMED = [];
13511
13512 var EMPTY = {};
13513
13514 var VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
13515
13516 renderToString.render = renderToString;
13517
13518 var shallowRender = function shallowRender(vnode, context) {
13519 return renderToString(vnode, context, SHALLOW);
13520 };
13521
13522 function renderToString(vnode, context, opts, inner, isSvgMode) {
13523 var _ref = vnode || EMPTY,
13524 nodeName = _ref.nodeName,
13525 attributes = _ref.attributes,
13526 children = _ref.children,
13527 isComponent = false;
13528
13529 context = context || {};
13530 opts = opts || {};
13531
13532 var pretty = opts.pretty,
13533 indentChar = typeof pretty === 'string' ? pretty : '\t';
13534
13535 if (vnode == null || typeof vnode === 'boolean') {
13536 return '';
13537 }
13538
13539 if ((typeof vnode === 'undefined' ? 'undefined' : _typeof(vnode)) !== 'object' && !nodeName) {
13540 return encodeEntities(vnode);
13541 }
13542
13543 if (typeof nodeName === 'function') {
13544 isComponent = true;
13545 if (opts.shallow && (inner || opts.renderRootComponent === false)) {
13546 nodeName = getComponentName(nodeName);
13547 } else {
13548 var props = getNodeProps(vnode),
13549 rendered = void 0;
13550
13551 if (!nodeName.prototype || typeof nodeName.prototype.render !== 'function') {
13552 rendered = nodeName(props, context);
13553 } else {
13554 var c = new nodeName(props, context);
13555
13556 c._disable = c.__x = true;
13557 c.props = props;
13558 c.context = context;
13559 if (c.componentWillMount) c.componentWillMount();
13560 rendered = c.render(c.props, c.state, c.context);
13561
13562 if (c.getChildContext) {
13563 context = assign(assign({}, context), c.getChildContext());
13564 }
13565 }
13566
13567 return renderToString(rendered, context, opts, opts.shallowHighOrder !== false);
13568 }
13569 }
13570
13571 var s = '',
13572 html = void 0;
13573
13574 if (attributes) {
13575 var attrs = objectKeys(attributes);
13576
13577 if (opts && opts.sortAttributes === true) attrs.sort();
13578
13579 for (var i = 0; i < attrs.length; i++) {
13580 var name = attrs[i],
13581 v = attributes[name];
13582 if (name === 'children') continue;
13583 if (!(opts && opts.allAttributes) && (name === 'key' || name === 'ref')) continue;
13584
13585 if (name === 'className') {
13586 if (attributes['class']) continue;
13587 name = 'class';
13588 } else if (isSvgMode && name.match(/^xlink\:?(.+)/)) {
13589 name = name.toLowerCase().replace(/^xlink\:?(.+)/, 'xlink:$1');
13590 }
13591
13592 if (name === 'class' && v && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object') {
13593 v = hashToClassName(v);
13594 } else if (name === 'style' && v && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object') {
13595 v = styleObjToCss(v);
13596 }
13597
13598 var hooked = opts.attributeHook && opts.attributeHook(name, v, context, opts, isComponent);
13599 if (hooked || hooked === '') {
13600 s += hooked;
13601 continue;
13602 }
13603
13604 if (name === 'dangerouslySetInnerHTML') {
13605 html = v && v.__html;
13606 } else if ((v || v === 0 || v === '') && typeof v !== 'function') {
13607 if (v === true || v === '') {
13608 v = name;
13609
13610 if (!opts || !opts.xml) {
13611 s += ' ' + name;
13612 continue;
13613 }
13614 }
13615 s += ' ' + name + '="' + encodeEntities(v) + '"';
13616 }
13617 }
13618 }
13619
13620 var sub = s.replace(/^\n\s*/, ' ');
13621 if (sub !== s && !~sub.indexOf('\n')) s = sub;else if (pretty && ~s.indexOf('\n')) s += '\n';
13622
13623 s = '<' + nodeName + s + '>';
13624
13625 if (VOID_ELEMENTS.indexOf(nodeName) > -1) {
13626 s = s.replace(/>$/, ' />');
13627 }
13628
13629 if (html) {
13630 if (pretty && isLargeString(html)) {
13631 html = '\n' + indentChar + indent(html, indentChar);
13632 }
13633 s += html;
13634 } else {
13635 var len = children && children.length,
13636 pieces = [],
13637 hasLarge = ~s.indexOf('\n');
13638 for (var _i = 0; _i < len; _i++) {
13639 var child = children[_i];
13640 if (!falsey(child)) {
13641 var childSvgMode = nodeName === 'svg' ? true : nodeName === 'foreignObject' ? false : isSvgMode,
13642 ret = renderToString(child, context, opts, true, childSvgMode);
13643 if (!hasLarge && pretty && isLargeString(ret)) hasLarge = true;
13644 if (ret) pieces.push(ret);
13645 }
13646 }
13647 if (pretty && hasLarge) {
13648 for (var _i2 = pieces.length; _i2--;) {
13649 pieces[_i2] = '\n' + indentChar + indent(pieces[_i2], indentChar);
13650 }
13651 }
13652 if (pieces.length) {
13653 s += pieces.join('');
13654 } else if (opts && opts.xml) {
13655 return s.substring(0, s.length - 1) + ' />';
13656 }
13657 }
13658
13659 if (VOID_ELEMENTS.indexOf(nodeName) === -1) {
13660 if (pretty && ~s.indexOf('\n')) s += '\n';
13661 s += '</' + nodeName + '>';
13662 }
13663
13664 return s;
13665 }
13666
13667 function getComponentName(component) {
13668 return component.displayName || component !== Function && component.name || getFallbackComponentName(component);
13669 }
13670
13671 function getFallbackComponentName(component) {
13672 var str = Function.prototype.toString.call(component),
13673 name = (str.match(/^\s*function\s+([^\( ]+)/) || EMPTY)[1];
13674 if (!name) {
13675 var index = -1;
13676 for (var i = UNNAMED.length; i--;) {
13677 if (UNNAMED[i] === component) {
13678 index = i;
13679 break;
13680 }
13681 }
13682
13683 if (index < 0) {
13684 index = UNNAMED.push(component) - 1;
13685 }
13686 name = 'UnnamedComponent' + index;
13687 }
13688 return name;
13689 }
13690 renderToString.shallowRender = shallowRender;
13691
13692 return renderToString;
13693
13694 })));
13695
13696
13697 },{}],20:[function(require,module,exports){
13698 !function() {
13699 'use strict';
13700 function VNode() {}
13701 function h(nodeName, attributes) {
13702 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13703 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13704 if (attributes && null != attributes.children) {
13705 if (!stack.length) stack.push(attributes.children);
13706 delete attributes.children;
13707 }
13708 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13709 if ('boolean' == typeof child) child = null;
13710 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13711 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13712 lastSimple = simple;
13713 }
13714 var p = new VNode();
13715 p.nodeName = nodeName;
13716 p.children = children;
13717 p.attributes = null == attributes ? void 0 : attributes;
13718 p.key = null == attributes ? void 0 : attributes.key;
13719 if (void 0 !== options.vnode) options.vnode(p);
13720 return p;
13721 }
13722 function extend(obj, props) {
13723 for (var i in props) obj[i] = props[i];
13724 return obj;
13725 }
13726 function cloneElement(vnode, props) {
13727 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13728 }
13729 function enqueueRender(component) {
13730 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13731 }
13732 function rerender() {
13733 var p, list = items;
13734 items = [];
13735 while (p = list.pop()) if (p.__d) renderComponent(p);
13736 }
13737 function isSameNodeType(node, vnode, hydrating) {
13738 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13739 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13740 }
13741 function isNamedNode(node, nodeName) {
13742 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
13743 }
13744 function getNodeProps(vnode) {
13745 var props = extend({}, vnode.attributes);
13746 props.children = vnode.children;
13747 var defaultProps = vnode.nodeName.defaultProps;
13748 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
13749 return props;
13750 }
13751 function createNode(nodeName, isSvg) {
13752 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
13753 node.__n = nodeName;
13754 return node;
13755 }
13756 function removeNode(node) {
13757 var parentNode = node.parentNode;
13758 if (parentNode) parentNode.removeChild(node);
13759 }
13760 function setAccessor(node, name, old, value, isSvg) {
13761 if ('className' === name) name = 'class';
13762 if ('key' === name) ; else if ('ref' === name) {
13763 if (old) old(null);
13764 if (value) value(node);
13765 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
13766 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
13767 if (value && 'object' == typeof value) {
13768 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
13769 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
13770 }
13771 } else if ('dangerouslySetInnerHTML' === name) {
13772 if (value) node.innerHTML = value.__html || '';
13773 } else if ('o' == name[0] && 'n' == name[1]) {
13774 var useCapture = name !== (name = name.replace(/Capture$/, ''));
13775 name = name.toLowerCase().substring(2);
13776 if (value) {
13777 if (!old) node.addEventListener(name, eventProxy, useCapture);
13778 } else node.removeEventListener(name, eventProxy, useCapture);
13779 (node.__l || (node.__l = {}))[name] = value;
13780 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
13781 setProperty(node, name, null == value ? '' : value);
13782 if (null == value || !1 === value) node.removeAttribute(name);
13783 } else {
13784 var ns = isSvg && name !== (name = name.replace(/^xlink\:?/, ''));
13785 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);
13786 }
13787 }
13788 function setProperty(node, name, value) {
13789 try {
13790 node[name] = value;
13791 } catch (e) {}
13792 }
13793 function eventProxy(e) {
13794 return this.__l[e.type](options.event && options.event(e) || e);
13795 }
13796 function flushMounts() {
13797 var c;
13798 while (c = mounts.pop()) {
13799 if (options.afterMount) options.afterMount(c);
13800 if (c.componentDidMount) c.componentDidMount();
13801 }
13802 }
13803 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
13804 if (!diffLevel++) {
13805 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
13806 hydrating = null != dom && !('__preactattr_' in dom);
13807 }
13808 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
13809 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
13810 if (!--diffLevel) {
13811 hydrating = !1;
13812 if (!componentRoot) flushMounts();
13813 }
13814 return ret;
13815 }
13816 function idiff(dom, vnode, context, mountAll, componentRoot) {
13817 var out = dom, prevSvgMode = isSvgMode;
13818 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
13819 if ('string' == typeof vnode || 'number' == typeof vnode) {
13820 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
13821 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
13822 } else {
13823 out = document.createTextNode(vnode);
13824 if (dom) {
13825 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13826 recollectNodeTree(dom, !0);
13827 }
13828 }
13829 out.__preactattr_ = !0;
13830 return out;
13831 }
13832 var vnodeName = vnode.nodeName;
13833 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
13834 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
13835 vnodeName = String(vnodeName);
13836 if (!dom || !isNamedNode(dom, vnodeName)) {
13837 out = createNode(vnodeName, isSvgMode);
13838 if (dom) {
13839 while (dom.firstChild) out.appendChild(dom.firstChild);
13840 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13841 recollectNodeTree(dom, !0);
13842 }
13843 }
13844 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
13845 if (null == props) {
13846 props = out.__preactattr_ = {};
13847 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
13848 }
13849 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
13850 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
13851 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
13852 diffAttributes(out, vnode.attributes, props);
13853 isSvgMode = prevSvgMode;
13854 return out;
13855 }
13856 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
13857 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;
13858 if (0 !== len) for (var i = 0; i < len; i++) {
13859 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
13860 if (null != key) {
13861 keyedLen++;
13862 keyed[key] = _child;
13863 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
13864 }
13865 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
13866 vchild = vchildren[i];
13867 child = null;
13868 var key = vchild.key;
13869 if (null != key) {
13870 if (keyedLen && void 0 !== keyed[key]) {
13871 child = keyed[key];
13872 keyed[key] = void 0;
13873 keyedLen--;
13874 }
13875 } else if (!child && min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
13876 child = c;
13877 children[j] = void 0;
13878 if (j === childrenLen - 1) childrenLen--;
13879 if (j === min) min++;
13880 break;
13881 }
13882 child = idiff(child, vchild, context, mountAll);
13883 f = originalChildren[i];
13884 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
13885 }
13886 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
13887 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
13888 }
13889 function recollectNodeTree(node, unmountOnly) {
13890 var component = node._component;
13891 if (component) unmountComponent(component); else {
13892 if (null != node.__preactattr_ && node.__preactattr_.ref) node.__preactattr_.ref(null);
13893 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
13894 removeChildren(node);
13895 }
13896 }
13897 function removeChildren(node) {
13898 node = node.lastChild;
13899 while (node) {
13900 var next = node.previousSibling;
13901 recollectNodeTree(node, !0);
13902 node = next;
13903 }
13904 }
13905 function diffAttributes(dom, attrs, old) {
13906 var name;
13907 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
13908 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);
13909 }
13910 function collectComponent(component) {
13911 var name = component.constructor.name;
13912 (components[name] || (components[name] = [])).push(component);
13913 }
13914 function createComponent(Ctor, props, context) {
13915 var inst, list = components[Ctor.name];
13916 if (Ctor.prototype && Ctor.prototype.render) {
13917 inst = new Ctor(props, context);
13918 Component.call(inst, props, context);
13919 } else {
13920 inst = new Component(props, context);
13921 inst.constructor = Ctor;
13922 inst.render = doRender;
13923 }
13924 if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) {
13925 inst.__b = list[i].__b;
13926 list.splice(i, 1);
13927 break;
13928 }
13929 return inst;
13930 }
13931 function doRender(props, state, context) {
13932 return this.constructor(props, context);
13933 }
13934 function setComponentProps(component, props, opts, context, mountAll) {
13935 if (!component.__x) {
13936 component.__x = !0;
13937 if (component.__r = props.ref) delete props.ref;
13938 if (component.__k = props.key) delete props.key;
13939 if (!component.base || mountAll) {
13940 if (component.componentWillMount) component.componentWillMount();
13941 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
13942 if (context && context !== component.context) {
13943 if (!component.__c) component.__c = component.context;
13944 component.context = context;
13945 }
13946 if (!component.__p) component.__p = component.props;
13947 component.props = props;
13948 component.__x = !1;
13949 if (0 !== opts) if (1 === opts || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
13950 if (component.__r) component.__r(component);
13951 }
13952 }
13953 function renderComponent(component, opts, mountAll, isChild) {
13954 if (!component.__x) {
13955 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;
13956 if (isUpdate) {
13957 component.props = previousProps;
13958 component.state = previousState;
13959 component.context = previousContext;
13960 if (2 !== opts && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
13961 component.props = props;
13962 component.state = state;
13963 component.context = context;
13964 }
13965 component.__p = component.__s = component.__c = component.__b = null;
13966 component.__d = !1;
13967 if (!skip) {
13968 rendered = component.render(props, state, context);
13969 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
13970 var toUnmount, base, childComponent = rendered && rendered.nodeName;
13971 if ('function' == typeof childComponent) {
13972 var childProps = getNodeProps(rendered);
13973 inst = initialChildComponent;
13974 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
13975 toUnmount = inst;
13976 component._component = inst = createComponent(childComponent, childProps, context);
13977 inst.__b = inst.__b || nextBase;
13978 inst.__u = component;
13979 setComponentProps(inst, childProps, 0, context, !1);
13980 renderComponent(inst, 1, mountAll, !0);
13981 }
13982 base = inst.base;
13983 } else {
13984 cbase = initialBase;
13985 toUnmount = initialChildComponent;
13986 if (toUnmount) cbase = component._component = null;
13987 if (initialBase || 1 === opts) {
13988 if (cbase) cbase._component = null;
13989 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
13990 }
13991 }
13992 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
13993 var baseParent = initialBase.parentNode;
13994 if (baseParent && base !== baseParent) {
13995 baseParent.replaceChild(base, initialBase);
13996 if (!toUnmount) {
13997 initialBase._component = null;
13998 recollectNodeTree(initialBase, !1);
13999 }
14000 }
14001 }
14002 if (toUnmount) unmountComponent(toUnmount);
14003 component.base = base;
14004 if (base && !isChild) {
14005 var componentRef = component, t = component;
14006 while (t = t.__u) (componentRef = t).base = base;
14007 base._component = componentRef;
14008 base._componentConstructor = componentRef.constructor;
14009 }
14010 }
14011 if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) {
14012 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext);
14013 if (options.afterUpdate) options.afterUpdate(component);
14014 }
14015 if (null != component.__h) while (component.__h.length) component.__h.pop().call(component);
14016 if (!diffLevel && !isChild) flushMounts();
14017 }
14018 }
14019 function buildComponentFromVNode(dom, vnode, context, mountAll) {
14020 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
14021 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
14022 if (c && isOwner && (!mountAll || c._component)) {
14023 setComponentProps(c, props, 3, context, mountAll);
14024 dom = c.base;
14025 } else {
14026 if (originalComponent && !isDirectOwner) {
14027 unmountComponent(originalComponent);
14028 dom = oldDom = null;
14029 }
14030 c = createComponent(vnode.nodeName, props, context);
14031 if (dom && !c.__b) {
14032 c.__b = dom;
14033 oldDom = null;
14034 }
14035 setComponentProps(c, props, 1, context, mountAll);
14036 dom = c.base;
14037 if (oldDom && dom !== oldDom) {
14038 oldDom._component = null;
14039 recollectNodeTree(oldDom, !1);
14040 }
14041 }
14042 return dom;
14043 }
14044 function unmountComponent(component) {
14045 if (options.beforeUnmount) options.beforeUnmount(component);
14046 var base = component.base;
14047 component.__x = !0;
14048 if (component.componentWillUnmount) component.componentWillUnmount();
14049 component.base = null;
14050 var inner = component._component;
14051 if (inner) unmountComponent(inner); else if (base) {
14052 if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null);
14053 component.__b = base;
14054 removeNode(base);
14055 collectComponent(component);
14056 removeChildren(base);
14057 }
14058 if (component.__r) component.__r(null);
14059 }
14060 function Component(props, context) {
14061 this.__d = !0;
14062 this.context = context;
14063 this.props = props;
14064 this.state = this.state || {};
14065 }
14066 function render(vnode, parent, merge) {
14067 return diff(merge, vnode, {}, !1, parent, !1);
14068 }
14069 var options = {};
14070 var stack = [];
14071 var EMPTY_CHILDREN = [];
14072 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14073 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14074 var items = [];
14075 var mounts = [];
14076 var diffLevel = 0;
14077 var isSvgMode = !1;
14078 var hydrating = !1;
14079 var components = {};
14080 extend(Component.prototype, {
14081 setState: function(state, callback) {
14082 var s = this.state;
14083 if (!this.__s) this.__s = extend({}, s);
14084 extend(s, 'function' == typeof state ? state(s, this.props) : state);
14085 if (callback) (this.__h = this.__h || []).push(callback);
14086 enqueueRender(this);
14087 },
14088 forceUpdate: function(callback) {
14089 if (callback) (this.__h = this.__h || []).push(callback);
14090 renderComponent(this, 2);
14091 },
14092 render: function() {}
14093 });
14094 var preact = {
14095 h: h,
14096 createElement: h,
14097 cloneElement: cloneElement,
14098 Component: Component,
14099 render: render,
14100 rerender: rerender,
14101 options: options
14102 };
14103 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14104 }();
14105
14106 },{}],21:[function(require,module,exports){
14107 function tlite(getTooltipOpts) {
14108 document.addEventListener('mouseover', function (e) {
14109 var el = e.target;
14110 var opts = getTooltipOpts(el);
14111
14112 if (!opts) {
14113 el = el.parentElement;
14114 opts = el && getTooltipOpts(el);
14115 }
14116
14117 opts && tlite.show(el, opts, true);
14118 });
14119 }
14120
14121 tlite.show = function (el, opts, isAuto) {
14122 var fallbackAttrib = 'data-tlite';
14123 opts = opts || {};
14124
14125 (el.tooltip || Tooltip(el, opts)).show();
14126
14127 function Tooltip(el, opts) {
14128 var tooltipEl;
14129 var showTimer;
14130 var text;
14131
14132 el.addEventListener('mousedown', autoHide);
14133 el.addEventListener('mouseleave', autoHide);
14134
14135 function show() {
14136 text = el.title || el.getAttribute(fallbackAttrib) || text;
14137 el.title = '';
14138 el.setAttribute(fallbackAttrib, '');
14139 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
14140 }
14141
14142 function autoHide() {
14143 tlite.hide(el, true);
14144 }
14145
14146 function hide(isAutoHiding) {
14147 if (isAuto === isAutoHiding) {
14148 showTimer = clearTimeout(showTimer);
14149 var parent = tooltipEl && tooltipEl.parentNode;
14150 parent && parent.removeChild(tooltipEl);
14151 tooltipEl = undefined;
14152 }
14153 }
14154
14155 function fadeIn() {
14156 if (!tooltipEl) {
14157 tooltipEl = createTooltip(el, text, opts);
14158 }
14159 }
14160
14161 return el.tooltip = {
14162 show: show,
14163 hide: hide
14164 };
14165 }
14166
14167 function createTooltip(el, text, opts) {
14168 var tooltipEl = document.createElement('span');
14169 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
14170
14171 tooltipEl.innerHTML = text;
14172
14173 el.appendChild(tooltipEl);
14174
14175 var vertGrav = grav[0] || '';
14176 var horzGrav = grav[1] || '';
14177
14178 function positionTooltip() {
14179 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
14180
14181 var arrowSize = 10;
14182 var top = el.offsetTop;
14183 var left = el.offsetLeft;
14184
14185 if (tooltipEl.offsetParent === el) {
14186 top = left = 0;
14187 }
14188
14189 var width = el.offsetWidth;
14190 var height = el.offsetHeight;
14191 var tooltipHeight = tooltipEl.offsetHeight;
14192 var tooltipWidth = tooltipEl.offsetWidth;
14193 var centerEl = left + (width / 2);
14194
14195 tooltipEl.style.top = (
14196 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
14197 vertGrav === 'n' ? (top + height + arrowSize) :
14198 (top + (height / 2) - (tooltipHeight / 2))
14199 ) + 'px';
14200
14201 tooltipEl.style.left = (
14202 horzGrav === 'w' ? left :
14203 horzGrav === 'e' ? left + width - tooltipWidth :
14204 vertGrav === 'w' ? (left + width + arrowSize) :
14205 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
14206 (centerEl - tooltipWidth / 2)
14207 ) + 'px';
14208 }
14209
14210 positionTooltip();
14211
14212 var rect = tooltipEl.getBoundingClientRect();
14213
14214 if (vertGrav === 's' && rect.top < 0) {
14215 vertGrav = 'n';
14216 positionTooltip();
14217 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
14218 vertGrav = 's';
14219 positionTooltip();
14220 } else if (vertGrav === 'e' && rect.left < 0) {
14221 vertGrav = 'w';
14222 positionTooltip();
14223 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
14224 vertGrav = 'e';
14225 positionTooltip();
14226 }
14227
14228 tooltipEl.className += ' tlite-visible';
14229
14230 return tooltipEl;
14231 }
14232 };
14233
14234 tlite.hide = function (el, isAuto) {
14235 el.tooltip && el.tooltip.hide(isAuto);
14236 };
14237
14238 if (typeof module !== 'undefined' && module.exports) {
14239 module.exports = tlite;
14240 }
14241
14242 },{}]},{},[2]);
14243 ; })();