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

14,365 lines 544.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined;(function 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})({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 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
53
54 _tabs2.default.init();
55 _actionConfirmations2.default.init();
56
57 if (document.getElementById('hf-form-editor')) {
58 _formEditor2.default.init();
59 _formActions2.default.init();
60
61 _fieldBuilder2.default.init(_formEditor2.default);
62 }
63
64 },{"./action-confirmations.js":1,"./field-builder.js":3,"./form-actions.js":6,"./form-editor.js":7,"./tabs.js":8}],3:[function(require,module,exports){
65 'use strict';
66
67 Object.defineProperty(exports, "__esModule", {
68 value: true
69 });
70
71 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; }; }();
72
73 var _desc, _value, _class, _desc2, _value2, _class2;
74
75 var _preact = require('preact');
76
77 var _linkstate = require('linkstate');
78
79 var _linkstate2 = _interopRequireDefault(_linkstate);
80
81 var _configFields = require('./field-builder/config-fields.js');
82
83 var _html = require('./field-builder/html.js');
84
85 var _decko = require('decko');
86
87 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
88
89 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
90
91 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; }
92
93 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; }
94
95 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
96 var desc = {};
97 Object['ke' + 'ys'](descriptor).forEach(function (key) {
98 desc[key] = descriptor[key];
99 });
100 desc.enumerable = !!desc.enumerable;
101 desc.configurable = !!desc.configurable;
102
103 if ('value' in desc || desc.initializer) {
104 desc.writable = true;
105 }
106
107 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
108 return decorator(target, property, desc) || desc;
109 }, desc);
110
111 if (context && desc.initializer !== void 0) {
112 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
113 desc.initializer = undefined;
114 }
115
116 if (desc.initializer === void 0) {
117 Object['define' + 'Property'](target, property, desc);
118 desc = null;
119 }
120
121 return desc;
122 }
123
124 var Editor = void 0;
125
126
127 var fields = {
128 "text": "Text",
129 "email": "Email",
130 "url": "URL",
131 "number": "Number",
132 "date": "Date",
133 "textarea": "Textarea",
134 "dropdown": "Dropdown",
135 "checkboxes": "Checkboxes",
136 "radio-buttons": "Radio buttons",
137 "submit": "Submit button"
138 };
139
140 var FieldBuilder = (_class = function (_Component) {
141 _inherits(FieldBuilder, _Component);
142
143 function FieldBuilder() {
144 _classCallCheck(this, FieldBuilder);
145
146 var _this = _possibleConstructorReturn(this, (FieldBuilder.__proto__ || Object.getPrototypeOf(FieldBuilder)).call(this));
147
148 _this.state = {
149 fieldType: ""
150 };
151 return _this;
152 }
153
154 _createClass(FieldBuilder, [{
155 key: 'handleCancel',
156 value: function handleCancel() {
157 this.setState({
158 fieldType: ""
159 });
160 }
161 }, {
162 key: 'openFieldConfig',
163 value: function openFieldConfig(e) {
164 var newFieldType = e.target.value;
165
166 if (this.state.fieldType === newFieldType) {
167 this.setState({ fieldType: "" });
168 } else {
169 this.setState({ fieldType: newFieldType });
170 }
171 }
172 }, {
173 key: 'render',
174 value: function render(props, state) {
175 var _this2 = this;
176
177 var fieldButtons = Object.keys(fields).map(function (key) {
178 var label = fields[key];
179 return (0, _preact.h)(
180 'button',
181 { type: 'button', value: key, className: "button " + (state.fieldType === key ? "active" : ""), onClick: _this2.openFieldConfig },
182 label
183 );
184 });
185
186 return (0, _preact.h)(
187 'div',
188 { 'class': 'hf-field-builder' },
189 (0, _preact.h)(
190 'h4',
191 null,
192 'Add field'
193 ),
194 (0, _preact.h)(
195 'div',
196 { 'class': 'available-fields' },
197 fieldButtons
198 ),
199 (0, _preact.h)(
200 'div',
201 { style: 'max-width: 480px;' },
202 (0, _preact.h)(FieldConfigurator, { fieldType: state.fieldType, onCancel: this.handleCancel })
203 ),
204 state.fieldType === "" ? (0, _preact.h)(
205 'p',
206 { 'class': 'help', style: 'margin-bottom: 0;' },
207 'Use the buttons above to generate your field HTML, or manually modify your form below.'
208 ) : ""
209 );
210 }
211 }]);
212
213 return FieldBuilder;
214 }(_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);
215 var FieldConfigurator = (_class2 = function (_Component2) {
216 _inherits(FieldConfigurator, _Component2);
217
218 function FieldConfigurator(props) {
219 _classCallCheck(this, FieldConfigurator);
220
221 var _this3 = _possibleConstructorReturn(this, (FieldConfigurator.__proto__ || Object.getPrototypeOf(FieldConfigurator)).call(this, props));
222
223 _this3.state = _this3.getInitialState();
224 _this3.choiceHandlers = {
225 "add": _this3.addChoice,
226 "delete": _this3.deleteChoice,
227 "changeLabel": _this3.changeChoiceLabel,
228 "toggleChecked": _this3.toggleChoiceChecked
229 };
230 return _this3;
231 }
232
233 _createClass(FieldConfigurator, [{
234 key: 'getInitialState',
235 value: function getInitialState() {
236 return {
237 fieldType: this.props.fieldType,
238 fieldLabel: "",
239 placeholder: "",
240 value: "",
241 wrap: true,
242 required: false,
243 choices: [{
244 checked: false,
245 label: "One"
246 }, {
247 checked: false,
248 label: "Two"
249 }]
250 };
251 }
252 }, {
253 key: 'componentWillReceiveProps',
254 value: function componentWillReceiveProps(props) {
255 this.setState({
256 fieldType: props.fieldType
257 });
258 }
259 }, {
260 key: 'addToForm',
261 value: function addToForm() {
262 var html = (0, _html.htmlgenerate)(this.state);
263 Editor.replaceSelection(html);
264 }
265 }, {
266 key: 'addChoice',
267 value: function addChoice() {
268 var arr = this.state.choices;
269 arr.push({ checked: false, label: "..." });
270 this.setState({ choices: arr });
271 }
272 }, {
273 key: 'deleteChoice',
274 value: function deleteChoice(e) {
275 var arr = this.state.choices;
276 var index = e.target.parentElement.getAttribute('data-key');
277 arr.splice(index, 1);
278 this.setState({ choices: arr });
279 }
280 }, {
281 key: 'changeChoiceLabel',
282 value: function changeChoiceLabel(e) {
283 var arr = this.state.choices;
284 var index = e.target.parentElement.getAttribute('data-key');
285 arr[index].label = e.target.value;
286 this.setState({ choices: arr });
287 }
288 }, {
289 key: 'toggleChoiceChecked',
290 value: function toggleChoiceChecked(e) {
291 var arr = this.state.choices;
292 var index = e.target.parentElement.getAttribute('data-key');
293 arr[index].checked = !arr[index].checked;
294 this.setState({ choices: arr });
295 }
296 }, {
297 key: 'handleCancel',
298 value: function handleCancel() {
299 // revert back to initial state
300 this.setState(this.getInitialState());
301 this.props.onCancel();
302 }
303 }, {
304 key: 'render',
305 value: function render(props, state) {
306 if (state.fieldType === "") {
307 return "";
308 }
309
310 var formFields = void 0;
311
312 switch (state.fieldType) {
313 case "text":
314 case "email":
315 case "url":
316 case "number":
317 case "textarea":
318 formFields = (0, _preact.h)(
319 'div',
320 null,
321 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
322 (0, _preact.h)(_configFields.Placeholder, { value: state.placeholder, onChange: (0, _linkstate2.default)(this, 'placeholder') }),
323 (0, _preact.h)(_configFields.DefaultValue, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }),
324 (0, _preact.h)(_configFields.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }),
325 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
326 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
327 );
328 break;
329 case "submit":
330 formFields = (0, _preact.h)(
331 'div',
332 null,
333 (0, _preact.h)(_configFields.ButtonText, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }),
334 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
335 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
336 );
337 break;
338
339 case "date":
340 formFields = (0, _preact.h)(
341 'div',
342 null,
343 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
344 (0, _preact.h)(_configFields.DefaultValue, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }),
345 (0, _preact.h)(_configFields.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }),
346 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
347 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
348 );
349 break;
350 case "dropdown":
351 formFields = (0, _preact.h)(
352 'div',
353 null,
354 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
355 (0, _preact.h)(_configFields.Choices, { multiple: false, choices: state.choices, handlers: this.choiceHandlers }),
356 (0, _preact.h)(_configFields.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }),
357 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
358 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
359 );
360 break;
361
362 case "radio-buttons":
363 case "checkboxes":
364 formFields = (0, _preact.h)(
365 'div',
366 null,
367 (0, _preact.h)(_configFields.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }),
368 (0, _preact.h)(_configFields.Choices, { multiple: state.fieldType === "checkboxes", choices: state.choices, handlers: this.choiceHandlers }),
369 (0, _preact.h)(_configFields.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }),
370 (0, _preact.h)(_configFields.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel })
371 );
372 break;
373 }
374
375 return (0, _preact.h)(
376 'div',
377 { 'class': 'field-config', onKeyPress: FieldConfigurator.handleKeyPress },
378 formFields
379 );
380 }
381 }], [{
382 key: 'handleKeyPress',
383 value: function handleKeyPress(e) {
384 // stop RETURN from submitting the parent form.
385 if (e.keyCode === 13) {
386 e.preventDefault();
387 }
388 }
389 }]);
390
391 return FieldConfigurator;
392 }(_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);
393 exports.default = {
394 init: function init(editor) {
395 Editor = editor;
396
397 (0, _preact.render)((0, _preact.h)(FieldBuilder, null), document.getElementById('hf-field-builder'));
398 }
399 };
400
401 },{"./field-builder/config-fields.js":4,"./field-builder/html.js":5,"decko":17,"linkstate":19,"preact":21}],4:[function(require,module,exports){
402 "use strict";
403
404 Object.defineProperty(exports, "__esModule", {
405 value: true
406 });
407 exports.ButtonText = exports.Choices = exports.Required = exports.Wrap = exports.DefaultValue = exports.Placeholder = exports.Label = exports.AddToForm = undefined;
408
409 var _preact = require("preact");
410
411 function AddToForm(props) {
412 return (0, _preact.h)(
413 "div",
414 { "class": "hf-small-margin" },
415 (0, _preact.h)(
416 "button",
417 { "class": "button", type: "button", onClick: props.onSubmit },
418 "Add field to form"
419 ),
420 " \xA0 ",
421 (0, _preact.h)(
422 "a",
423 { href: "javascript:void(0);", "class": "hf-small", style: "vertical-align: middle;", onClick: props.onCancel },
424 "or close field helper"
425 )
426 );
427 }
428
429 function Label(props) {
430 return (0, _preact.h)(
431 "div",
432 { "class": "hf-small-margin" },
433 (0, _preact.h)(
434 "label",
435 { "for": "hf-fg-field-label" },
436 "Field label ",
437 (0, _preact.h)(
438 "span",
439 { "class": "hf-required" },
440 "*"
441 )
442 ),
443 (0, _preact.h)("input", { id: "hf-fg-field-label", type: "text", value: props.value, onChange: props.onChange })
444 );
445 }
446
447 function Placeholder(props) {
448 return (0, _preact.h)(
449 "div",
450 { "class": "hf-small-margin" },
451 (0, _preact.h)(
452 "label",
453 { "for": "hf-fg-placeholder" },
454 "Placeholder ",
455 (0, _preact.h)(
456 "span",
457 { "class": "hf-italic hf-pull-right" },
458 "Optional"
459 )
460 ),
461 (0, _preact.h)("input", { id: "hf-fg-placeholder", type: "text", value: props.value, onChange: props.onChange }),
462 (0, _preact.h)(
463 "p",
464 { "class": "help" },
465 "Text to show when field has no value."
466 )
467 );
468 }
469
470 function ButtonText(props) {
471 return (0, _preact.h)(
472 "div",
473 { "class": "hf-small-margin" },
474 (0, _preact.h)(
475 "label",
476 { "for": "hf-fg-default-value" },
477 "Button text ",
478 (0, _preact.h)(
479 "span",
480 { "class": "hf-required" },
481 "*"
482 )
483 ),
484 (0, _preact.h)("input", { id: "hf-fg-default-value", type: "text", value: props.value, onChange: props.onChange }),
485 (0, _preact.h)(
486 "p",
487 { "class": "help" },
488 "Text to show on the button."
489 )
490 );
491 }
492
493 function DefaultValue(props) {
494 return (0, _preact.h)(
495 "div",
496 { "class": "hf-small-margin" },
497 (0, _preact.h)(
498 "label",
499 { "for": "hf-fg-default-value" },
500 "Default value ",
501 (0, _preact.h)(
502 "span",
503 { "class": "hf-italic hf-pull-right" },
504 "Optional"
505 )
506 ),
507 (0, _preact.h)("input", { id: "hf-fg-default-value", type: "text", value: props.value, onChange: props.onChange }),
508 (0, _preact.h)(
509 "p",
510 { "class": "help" },
511 "Text to pre-fill this field with."
512 )
513 );
514 }
515
516 function Wrap(props) {
517 return (0, _preact.h)(
518 "div",
519 { "class": "hf-small-margin" },
520 (0, _preact.h)(
521 "label",
522 { "class": "inline" },
523 (0, _preact.h)("input", { type: "checkbox", value: "1", defaultChecked: props.checked, onChange: props.onChange }),
524 "Wrap this field in paragraph tags."
525 )
526 );
527 }
528
529 function Required(props) {
530 return (0, _preact.h)(
531 "div",
532 { "class": "hf-small-margin" },
533 (0, _preact.h)(
534 "label",
535 { "class": "inline" },
536 (0, _preact.h)("input", { type: "checkbox", value: "1", defaultChecked: props.checked, onChange: props.onChange }),
537 "This field is required."
538 )
539 );
540 }
541
542 function Choices(props) {
543 var choiceFields = props.choices.map(function (choice, k) {
544 return (0, _preact.h)(
545 "div",
546 { "data-key": k },
547 (0, _preact.h)("input", { type: props.multiple ? "checkbox" : "radio", name: "selected", defaultChecked: choice.checked, onChange: props.handlers.toggleChecked, title: "Pre-select this choice?" }),
548 (0, _preact.h)("input", { type: "text", value: choice.label, placeholder: "Choice label", style: "width: 80%;", onChange: props.handlers.changeLabel }),
549 (0, _preact.h)(
550 "a",
551 { href: "javascript:void(0);", onClick: props.handlers.delete, style: "text-decoration: none;", title: "Delete choice" },
552 "\u2715"
553 )
554 );
555 });
556
557 return (0, _preact.h)(
558 "div",
559 { "class": "hf-small-margin" },
560 (0, _preact.h)(
561 "label",
562 null,
563 "Choices"
564 ),
565 choiceFields,
566 (0, _preact.h)("input", { type: props.multiple ? "checkbox" : "radio", style: "visibility: hidden;" }),
567 (0, _preact.h)(
568 "a",
569 { href: "javascript:void(0);", onClick: props.handlers.add },
570 "Add choice"
571 )
572 );
573 }
574
575 exports.AddToForm = AddToForm;
576 exports.Label = Label;
577 exports.Placeholder = Placeholder;
578 exports.DefaultValue = DefaultValue;
579 exports.Wrap = Wrap;
580 exports.Required = Required;
581 exports.Choices = Choices;
582 exports.ButtonText = ButtonText;
583
584 },{"preact":21}],5:[function(require,module,exports){
585 'use strict';
586
587 Object.defineProperty(exports, "__esModule", {
588 value: true
589 });
590 exports.htmlgenerate = undefined;
591
592 var _html = require('html');
593
594 var _html2 = _interopRequireDefault(_html);
595
596 var _preactRenderToString = require('preact-render-to-string');
597
598 var _preactRenderToString2 = _interopRequireDefault(_preactRenderToString);
599
600 var _preact = require('preact');
601
602 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
603
604 function htmlgenerate(conf) {
605 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)("label", {}, conf.fieldLabel) : "";
606 var fieldAttr = void 0,
607 field = void 0;
608
609 switch (conf.fieldType) {
610 case "text":
611 default:
612 fieldAttr = {
613 type: conf.fieldType,
614 name: namify(conf.fieldLabel),
615 value: conf.value,
616 placeholder: conf.placeholder,
617 required: conf.required
618 };
619 field = html("input", fieldAttr);
620 break;
621 case "textarea":
622 fieldAttr = {
623 name: namify(conf.fieldLabel),
624 placeholder: conf.placeholder,
625 required: conf.required
626 };
627 field = html("textarea", fieldAttr, conf.value);
628 break;
629
630 case "dropdown":
631 fieldAttr = {
632 name: namify(conf.fieldLabel),
633 required: conf.required
634 };
635 var opts = conf.choices.map(function (choice) {
636 return html("option", { defaultChecked: choice.checked }, choice.label);
637 });
638 field = html("select", fieldAttr, opts);
639 break;
640
641 case "radio-buttons":
642 field = conf.choices.map(function (choice) {
643 return html("label", {}, [html("input", {
644 type: "radio",
645 name: namify(conf.fieldLabel),
646 value: choice.label,
647 selected: choice.checked
648 }), " ", html("span", {}, choice.label)]);
649 });
650 break;
651
652 case "checkboxes":
653 field = conf.choices.map(function (choice) {
654 return html("label", {}, [html("input", {
655 type: "checkbox",
656 name: namify(conf.fieldLabel) + "[]",
657 value: choice.label,
658 checked: choice.checked
659 }), " ", html("span", {}, choice.label)]);
660 });
661 break;
662
663 case "submit":
664 fieldAttr = {
665 type: "submit",
666 value: conf.value
667 };
668 field = html("input", fieldAttr);
669 break;
670
671 }
672
673 var str = "";
674 if (conf.wrap) {
675 var tmpl = (0, _preact.h)("p", {}, [label, field]);
676 str = (0, _preactRenderToString2.default)(tmpl);
677 } else {
678 str += (0, _preactRenderToString2.default)(label);
679 str += (0, _preactRenderToString2.default)(field);
680 }
681
682 str = _html2.default.prettyPrint(str);
683
684 return str;
685 }
686
687 function html(tag, attr, children) {
688 return (0, _preact.h)(tag, filterEmptyObjectValues(attr), children);
689 }
690
691 function namify(str) {
692 return str.replace(/ /g, '_').replace(/[^\w\[\]_]*/g, "").toUpperCase();
693 }
694
695 function filterEmptyObjectValues(obj) {
696 var newObj = {};
697 for (var propName in obj) {
698 if (obj[propName] !== false && obj[propName] !== "") {
699 newObj[propName] = obj[propName];
700 }
701 }
702 return newObj;
703 }
704
705 exports.htmlgenerate = htmlgenerate;
706
707 },{"html":18,"preact":21,"preact-render-to-string":20}],6:[function(require,module,exports){
708 'use strict';
709
710 Object.defineProperty(exports, "__esModule", {
711 value: true
712 });
713 var availableActions = void 0,
714 actionTemplates = void 0,
715 actions = void 0;
716
717 function init() {
718 actions = document.getElementById('hf-form-actions');
719 availableActions = document.getElementById('hf-available-form-actions');
720 actionTemplates = document.getElementById('hf-form-action-templates');
721
722 // turn settings into accordions
723 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
724 el.parentNode.removeChild(el);
725
726 var heading = el.getAttribute('data-title');
727 var summary = el.querySelector('.hf-action-summary');
728 if (summary) {
729 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
730 }
731 var wrap = createAccordion(heading, el.innerHTML);
732 actions.appendChild(wrap);
733
734 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
735 });
736
737 availableActions.addEventListener('click', addAction, true);
738 }
739
740 function createAccordion(headingHTML, contentHTML) {
741 var wrap = document.createElement('div');
742 wrap.className = "hf-accordion expanded ";
743
744 var heading = document.createElement('h4');
745 heading.className = "hf-accordion-heading";
746 heading.innerHTML = headingHTML;
747 wrap.appendChild(heading);
748
749 var content = document.createElement('div');
750 content.className = "hf-accordion-content";
751 content.innerHTML = contentHTML;
752 wrap.appendChild(content);
753
754 var deleteWrap = document.createElement('p');
755 deleteWrap.style.textAlign = 'right';
756 var deleteLink = document.createElement('a');
757 deleteLink.href = 'javascript:void(0);';
758 deleteLink.className = "danger";
759 deleteLink.innerText = 'Delete this action';
760 deleteWrap.appendChild(deleteLink);
761 content.appendChild(deleteWrap);
762
763 // bind handlers
764 heading.addEventListener('click', createToggleActionHandler(wrap, content));
765 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
766 return wrap;
767 }
768
769 function addAction(e) {
770 var el = e.target || e.srcElement;
771 if (el.tagName !== 'INPUT') {
772 return;
773 }
774
775 var actionType = el.getAttribute('data-action-type');
776 var actionTemplate = actionTemplates.querySelector('#hf-action-type-' + actionType + '-template');
777
778 // append HTML to actions wrapper
779 var wrap = createAccordion(el.value, actionTemplate.innerHTML);
780 actions.appendChild(wrap);
781
782 // hide "no form actions" message
783 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
784 }
785
786 function createDeleteActionHandler(wrap) {
787 return function () {
788 actions.removeChild(wrap);
789
790 if (actions.childElementCount === 1) {
791 actions.querySelector('#hf-form-actions-empty').style.display = '';
792 }
793 };
794 }
795
796 function createToggleActionHandler(wrap, content) {
797 return function () {
798 var show = content.offsetParent === null;
799 wrap.className = wrap.className.replace('expanded', '');
800 if (show) {
801 wrap.className = wrap.className + " expanded";
802 }
803 content.style.display = show ? 'block' : 'none';
804 };
805 }
806
807 exports.default = {
808 'init': init
809 };
810
811 },{}],7:[function(require,module,exports){
812 'use strict';
813
814 // load CodeMirror & plugins
815
816 Object.defineProperty(exports, "__esModule", {
817 value: true
818 });
819 var CodeMirror = require('codemirror');
820 require('codemirror/mode/xml/xml');
821 require('codemirror/mode/javascript/javascript');
822 require('codemirror/mode/css/css');
823 require('codemirror/mode/htmlmixed/htmlmixed');
824 require('codemirror/addon/fold/xml-fold');
825 require('codemirror/addon/edit/matchtags');
826 require('codemirror/addon/edit/closetag.js');
827
828 var editor = void 0,
829 element = void 0,
830 dom = void 0,
831 requiredFieldsInput = void 0,
832 emailFieldsInput = void 0;
833
834 function init() {
835 element = document.getElementById('hf-form-editor');
836 dom = document.createElement('form');
837 requiredFieldsInput = document.getElementById('hf-required-fields');
838 emailFieldsInput = document.getElementById('hf-email-fields');
839
840 dom.innerHTML = element.value;
841
842 editor = CodeMirror.fromTextArea(element, {
843 selectionPointer: true,
844 matchTags: { bothTags: true },
845 mode: "htmlmixed",
846 htmlMode: true,
847 autoCloseTags: true,
848 autoRefresh: true,
849 styleActiveLine: true,
850 matchBrackets: true
851 });
852
853 editor.on('changes', debounce(updateShadowDOM, 100));
854 editor.on('blur', updateShadowDOM);
855 editor.on('blur', updateFieldVariables);
856 editor.on('blur', updateRequiredFields);
857 editor.on('blur', updateEmailFields);
858
859 document.getElementById('wpbody').addEventListener('click', updateFieldVariables);
860 }
861
862 function getFieldVariableName(f) {
863 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
864 }
865
866 function updateFieldVariables() {
867 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
868 var fieldVariables = uniq([].map.call(fields, function (f) {
869 return '[' + getFieldVariableName(f) + ']';
870 }));
871
872 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
873 while (el.firstChild) {
874 el.removeChild(el.firstChild);
875 }
876
877 var variableElements = fieldVariables.map(function (n) {
878 var el = document.createElement('code');
879 el.innerText = n;
880 return el;
881 });
882
883 variableElements.forEach(function (vel, i, arr) {
884 el.appendChild(vel);
885
886 if (i < arr.length - 1) {
887 el.appendChild(document.createTextNode(', '));
888 }
889 });
890 });
891 }
892
893 function updateShadowDOM() {
894 dom.innerHTML = editor.getValue();
895 }
896
897 function updateRequiredFields() {
898 var fields = dom.querySelectorAll('[required]');
899 var fieldNames = [].map.call(fields, getFieldVariableName);
900 requiredFieldsInput.value = fieldNames.join(',');
901 }
902
903 function updateEmailFields() {
904 var fields = dom.querySelectorAll('input[type="email"]');
905 var fieldNames = [].map.call(fields, getFieldVariableName);
906 emailFieldsInput.value = fieldNames.join(',');
907 }
908
909 function replaceSelection(str) {
910 editor.replaceSelection(str);
911 editor.focus();
912 }
913
914 function debounce(func, wait, immediate) {
915 var timeout;
916 return function () {
917 var context = this,
918 args = arguments;
919 var later = function later() {
920 timeout = null;
921 if (!immediate) func.apply(context, args);
922 };
923 var callNow = immediate && !timeout;
924 clearTimeout(timeout);
925 timeout = setTimeout(later, wait);
926 if (callNow) func.apply(context, args);
927 };
928 };
929
930 function uniq(a) {
931 var seen = {};
932 return a.filter(function (item) {
933 return seen.hasOwnProperty(item) ? false : seen[item] = true;
934 });
935 }
936
937 exports.default = {
938 'init': init,
939 'replaceSelection': replaceSelection
940 };
941
942 },{"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){
943 'use strict';
944
945 Object.defineProperty(exports, "__esModule", {
946 value: true
947 });
948 var tabs = void 0,
949 tabNavs = void 0;
950 var Tabs = {};
951
952 Tabs.init = function () {
953 tabs = document.querySelectorAll('.hf-tab');
954 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
955 for (var i = 0; i < tabNavs.length; i++) {
956 tabNavs[i].addEventListener('click', Tabs.open);
957 }
958 };
959
960 Tabs.open = function (e) {
961 var tabTarget = this.getAttribute('data-tab-target');
962 for (var i = 0; i < tabNavs.length; i++) {
963 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
964 }
965 this.blur();
966
967 for (var _i = 0; _i < tabs.length; _i++) {
968 var tab = tabs[_i];
969 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
970 }
971
972 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + " ");
973
974 e.preventDefault();
975 };
976
977 exports.default = Tabs;
978
979 },{}],9:[function(require,module,exports){
980 // CodeMirror, copyright (c) by Marijn Haverbeke and others
981 // Distributed under an MIT license: http://codemirror.net/LICENSE
982
983 /**
984 * Tag-closer extension for CodeMirror.
985 *
986 * This extension adds an "autoCloseTags" option that can be set to
987 * either true to get the default behavior, or an object to further
988 * configure its behavior.
989 *
990 * These are supported options:
991 *
992 * `whenClosing` (default true)
993 * Whether to autoclose when the '/' of a closing tag is typed.
994 * `whenOpening` (default true)
995 * Whether to autoclose the tag when the final '>' of an opening
996 * tag is typed.
997 * `dontCloseTags` (default is empty tags for HTML, none for XML)
998 * An array of tag names that should not be autoclosed.
999 * `indentTags` (default is block tags for HTML, none for XML)
1000 * An array of tag names that should, when opened, cause a
1001 * blank line to be added inside the tag, and the blank line and
1002 * closing line to be indented.
1003 *
1004 * See demos/closetag.html for a usage example.
1005 */
1006
1007 (function(mod) {
1008 if (typeof exports == "object" && typeof module == "object") // CommonJS
1009 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1010 else if (typeof define == "function" && define.amd) // AMD
1011 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1012 else // Plain browser env
1013 mod(CodeMirror);
1014 })(function(CodeMirror) {
1015 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1016 if (old != CodeMirror.Init && old)
1017 cm.removeKeyMap("autoCloseTags");
1018 if (!val) return;
1019 var map = {name: "autoCloseTags"};
1020 if (typeof val != "object" || val.whenClosing)
1021 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1022 if (typeof val != "object" || val.whenOpening)
1023 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1024 cm.addKeyMap(map);
1025 });
1026
1027 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1028 "source", "track", "wbr"];
1029 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1030 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1031
1032 function autoCloseGT(cm) {
1033 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1034 var ranges = cm.listSelections(), replacements = [];
1035 for (var i = 0; i < ranges.length; i++) {
1036 if (!ranges[i].empty()) return CodeMirror.Pass;
1037 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1038 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1039 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1040
1041 var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
1042 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1043 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1044
1045 var tagName = state.tagName;
1046 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1047 var lowerTagName = tagName.toLowerCase();
1048 // Don't process the '>' at the end of an end-tag or self-closing tag
1049 if (!tagName ||
1050 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1051 tok.type == "tag" && state.type == "closeTag" ||
1052 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1053 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1054 closingTagExists(cm, tagName, pos, state, true))
1055 return CodeMirror.Pass;
1056
1057 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1058 replacements[i] = {indent: indent,
1059 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1060 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1061 }
1062
1063 for (var i = ranges.length - 1; i >= 0; i--) {
1064 var info = replacements[i];
1065 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1066 var sel = cm.listSelections().slice(0);
1067 sel[i] = {head: info.newPos, anchor: info.newPos};
1068 cm.setSelections(sel);
1069 if (info.indent) {
1070 cm.indentLine(info.newPos.line, null, true);
1071 cm.indentLine(info.newPos.line + 1, null, true);
1072 }
1073 }
1074 }
1075
1076 function autoCloseCurrent(cm, typingSlash) {
1077 var ranges = cm.listSelections(), replacements = [];
1078 var head = typingSlash ? "/" : "</";
1079 for (var i = 0; i < ranges.length; i++) {
1080 if (!ranges[i].empty()) return CodeMirror.Pass;
1081 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1082 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1083 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1084 tok.start != pos.ch - 1))
1085 return CodeMirror.Pass;
1086 // Kludge to get around the fact that we are not in XML mode
1087 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1088 // work for other XML embedded languages (there is no general
1089 // way to go from a mixed mode to its current XML state).
1090 var replacement;
1091 if (inner.mode.name != "xml") {
1092 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1093 replacement = head + "script";
1094 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1095 replacement = head + "style";
1096 else
1097 return CodeMirror.Pass;
1098 } else {
1099 if (!state.context || !state.context.tagName ||
1100 closingTagExists(cm, state.context.tagName, pos, state))
1101 return CodeMirror.Pass;
1102 replacement = head + state.context.tagName;
1103 }
1104 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1105 replacements[i] = replacement;
1106 }
1107 cm.replaceSelections(replacements);
1108 ranges = cm.listSelections();
1109 for (var i = 0; i < ranges.length; i++)
1110 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1111 cm.indentLine(ranges[i].head.line);
1112 }
1113
1114 function autoCloseSlash(cm) {
1115 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1116 return autoCloseCurrent(cm, true);
1117 }
1118
1119 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1120
1121 function indexOf(collection, elt) {
1122 if (collection.indexOf) return collection.indexOf(elt);
1123 for (var i = 0, e = collection.length; i < e; ++i)
1124 if (collection[i] == elt) return i;
1125 return -1;
1126 }
1127
1128 // If xml-fold is loaded, we use its functionality to try and verify
1129 // whether a given tag is actually unclosed.
1130 function closingTagExists(cm, tagName, pos, state, newTag) {
1131 if (!CodeMirror.scanForClosingTag) return false;
1132 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1133 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1134 if (!nextClose || nextClose.tag != tagName) return false;
1135 var cx = state.context;
1136 // If the immediate wrapping context contains onCx instances of
1137 // the same tag, a closing tag only exists if there are at least
1138 // that many closing tags of that type following.
1139 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1140 pos = nextClose.to;
1141 for (var i = 1; i < onCx; i++) {
1142 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1143 if (!next || next.tag != tagName) return false;
1144 pos = next.to;
1145 }
1146 return true;
1147 }
1148 });
1149
1150 },{"../../lib/codemirror":12,"../fold/xml-fold":11}],10:[function(require,module,exports){
1151 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1152 // Distributed under an MIT license: http://codemirror.net/LICENSE
1153
1154 (function(mod) {
1155 if (typeof exports == "object" && typeof module == "object") // CommonJS
1156 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1157 else if (typeof define == "function" && define.amd) // AMD
1158 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1159 else // Plain browser env
1160 mod(CodeMirror);
1161 })(function(CodeMirror) {
1162 "use strict";
1163
1164 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1165 if (old && old != CodeMirror.Init) {
1166 cm.off("cursorActivity", doMatchTags);
1167 cm.off("viewportChange", maybeUpdateMatch);
1168 clear(cm);
1169 }
1170 if (val) {
1171 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1172 cm.on("cursorActivity", doMatchTags);
1173 cm.on("viewportChange", maybeUpdateMatch);
1174 doMatchTags(cm);
1175 }
1176 });
1177
1178 function clear(cm) {
1179 if (cm.state.tagHit) cm.state.tagHit.clear();
1180 if (cm.state.tagOther) cm.state.tagOther.clear();
1181 cm.state.tagHit = cm.state.tagOther = null;
1182 }
1183
1184 function doMatchTags(cm) {
1185 cm.state.failedTagMatch = false;
1186 cm.operation(function() {
1187 clear(cm);
1188 if (cm.somethingSelected()) return;
1189 var cur = cm.getCursor(), range = cm.getViewport();
1190 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1191 var match = CodeMirror.findMatchingTag(cm, cur, range);
1192 if (!match) return;
1193 if (cm.state.matchBothTags) {
1194 var hit = match.at == "open" ? match.open : match.close;
1195 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1196 }
1197 var other = match.at == "close" ? match.open : match.close;
1198 if (other)
1199 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1200 else
1201 cm.state.failedTagMatch = true;
1202 });
1203 }
1204
1205 function maybeUpdateMatch(cm) {
1206 if (cm.state.failedTagMatch) doMatchTags(cm);
1207 }
1208
1209 CodeMirror.commands.toMatchingTag = function(cm) {
1210 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1211 if (found) {
1212 var other = found.at == "close" ? found.open : found.close;
1213 if (other) cm.extendSelection(other.to, other.from);
1214 }
1215 };
1216 });
1217
1218 },{"../../lib/codemirror":12,"../fold/xml-fold":11}],11:[function(require,module,exports){
1219 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1220 // Distributed under an MIT license: http://codemirror.net/LICENSE
1221
1222 (function(mod) {
1223 if (typeof exports == "object" && typeof module == "object") // CommonJS
1224 mod(require("../../lib/codemirror"));
1225 else if (typeof define == "function" && define.amd) // AMD
1226 define(["../../lib/codemirror"], mod);
1227 else // Plain browser env
1228 mod(CodeMirror);
1229 })(function(CodeMirror) {
1230 "use strict";
1231
1232 var Pos = CodeMirror.Pos;
1233 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1234
1235 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";
1236 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1237 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1238
1239 function Iter(cm, line, ch, range) {
1240 this.line = line; this.ch = ch;
1241 this.cm = cm; this.text = cm.getLine(line);
1242 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1243 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1244 }
1245
1246 function tagAt(iter, ch) {
1247 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1248 return type && /\btag\b/.test(type);
1249 }
1250
1251 function nextLine(iter) {
1252 if (iter.line >= iter.max) return;
1253 iter.ch = 0;
1254 iter.text = iter.cm.getLine(++iter.line);
1255 return true;
1256 }
1257 function prevLine(iter) {
1258 if (iter.line <= iter.min) return;
1259 iter.text = iter.cm.getLine(--iter.line);
1260 iter.ch = iter.text.length;
1261 return true;
1262 }
1263
1264 function toTagEnd(iter) {
1265 for (;;) {
1266 var gt = iter.text.indexOf(">", iter.ch);
1267 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1268 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1269 var lastSlash = iter.text.lastIndexOf("/", gt);
1270 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1271 iter.ch = gt + 1;
1272 return selfClose ? "selfClose" : "regular";
1273 }
1274 }
1275 function toTagStart(iter) {
1276 for (;;) {
1277 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1278 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1279 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1280 xmlTagStart.lastIndex = lt;
1281 iter.ch = lt;
1282 var match = xmlTagStart.exec(iter.text);
1283 if (match && match.index == lt) return match;
1284 }
1285 }
1286
1287 function toNextTag(iter) {
1288 for (;;) {
1289 xmlTagStart.lastIndex = iter.ch;
1290 var found = xmlTagStart.exec(iter.text);
1291 if (!found) { if (nextLine(iter)) continue; else return; }
1292 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1293 iter.ch = found.index + found[0].length;
1294 return found;
1295 }
1296 }
1297 function toPrevTag(iter) {
1298 for (;;) {
1299 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1300 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1301 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1302 var lastSlash = iter.text.lastIndexOf("/", gt);
1303 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1304 iter.ch = gt + 1;
1305 return selfClose ? "selfClose" : "regular";
1306 }
1307 }
1308
1309 function findMatchingClose(iter, tag) {
1310 var stack = [];
1311 for (;;) {
1312 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1313 if (!next || !(end = toTagEnd(iter))) return;
1314 if (end == "selfClose") continue;
1315 if (next[1]) { // closing tag
1316 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1317 stack.length = i;
1318 break;
1319 }
1320 if (i < 0 && (!tag || tag == next[2])) return {
1321 tag: next[2],
1322 from: Pos(startLine, startCh),
1323 to: Pos(iter.line, iter.ch)
1324 };
1325 } else { // opening tag
1326 stack.push(next[2]);
1327 }
1328 }
1329 }
1330 function findMatchingOpen(iter, tag) {
1331 var stack = [];
1332 for (;;) {
1333 var prev = toPrevTag(iter);
1334 if (!prev) return;
1335 if (prev == "selfClose") { toTagStart(iter); continue; }
1336 var endLine = iter.line, endCh = iter.ch;
1337 var start = toTagStart(iter);
1338 if (!start) return;
1339 if (start[1]) { // closing tag
1340 stack.push(start[2]);
1341 } else { // opening tag
1342 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1343 stack.length = i;
1344 break;
1345 }
1346 if (i < 0 && (!tag || tag == start[2])) return {
1347 tag: start[2],
1348 from: Pos(iter.line, iter.ch),
1349 to: Pos(endLine, endCh)
1350 };
1351 }
1352 }
1353 }
1354
1355 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1356 var iter = new Iter(cm, start.line, 0);
1357 for (;;) {
1358 var openTag = toNextTag(iter), end;
1359 if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
1360 if (!openTag[1] && end != "selfClose") {
1361 var startPos = Pos(iter.line, iter.ch);
1362 var endPos = findMatchingClose(iter, openTag[2]);
1363 return endPos && {from: startPos, to: endPos.from};
1364 }
1365 }
1366 });
1367 CodeMirror.findMatchingTag = function(cm, pos, range) {
1368 var iter = new Iter(cm, pos.line, pos.ch, range);
1369 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1370 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1371 var start = end && toTagStart(iter);
1372 if (!end || !start || cmp(iter, pos) > 0) return;
1373 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1374 if (end == "selfClose") return {open: here, close: null, at: "open"};
1375
1376 if (start[1]) { // closing tag
1377 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1378 } else { // opening tag
1379 iter = new Iter(cm, to.line, to.ch, range);
1380 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1381 }
1382 };
1383
1384 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1385 var iter = new Iter(cm, pos.line, pos.ch, range);
1386 for (;;) {
1387 var open = findMatchingOpen(iter, tag);
1388 if (!open) break;
1389 var forward = new Iter(cm, pos.line, pos.ch, range);
1390 var close = findMatchingClose(forward, open.tag);
1391 if (close) return {open: open, close: close};
1392 }
1393 };
1394
1395 // Used by addon/edit/closetag.js
1396 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1397 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1398 return findMatchingClose(iter, name);
1399 };
1400 });
1401
1402 },{"../../lib/codemirror":12}],12:[function(require,module,exports){
1403 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1404 // Distributed under an MIT license: http://codemirror.net/LICENSE
1405
1406 // This is CodeMirror (http://codemirror.net), a code editor
1407 // implemented in JavaScript on top of the browser's DOM.
1408 //
1409 // You can find some technical background for some of the code below
1410 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1411
1412 (function (global, factory) {
1413 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1414 typeof define === 'function' && define.amd ? define(factory) :
1415 (global.CodeMirror = factory());
1416 }(this, (function () { 'use strict';
1417
1418 // Kludges for bugs and behavior differences that can't be feature
1419 // detected are enabled based on userAgent etc sniffing.
1420 var userAgent = navigator.userAgent;
1421 var platform = navigator.platform;
1422
1423 var gecko = /gecko\/\d/i.test(userAgent);
1424 var ie_upto10 = /MSIE \d/.test(userAgent);
1425 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1426 var edge = /Edge\/(\d+)/.exec(userAgent);
1427 var ie = ie_upto10 || ie_11up || edge;
1428 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1429 var webkit = !edge && /WebKit\//.test(userAgent);
1430 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1431 var chrome = !edge && /Chrome\//.test(userAgent);
1432 var presto = /Opera\//.test(userAgent);
1433 var safari = /Apple Computer/.test(navigator.vendor);
1434 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1435 var phantom = /PhantomJS/.test(userAgent);
1436
1437 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1438 var android = /Android/.test(userAgent);
1439 // This is woefully incomplete. Suggestions for alternative methods welcome.
1440 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1441 var mac = ios || /Mac/.test(platform);
1442 var chromeOS = /\bCrOS\b/.test(userAgent);
1443 var windows = /win/i.test(platform);
1444
1445 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1446 if (presto_version) { presto_version = Number(presto_version[1]); }
1447 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1448 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1449 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1450 var captureRightClick = gecko || (ie && ie_version >= 9);
1451
1452 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1453
1454 var rmClass = function(node, cls) {
1455 var current = node.className;
1456 var match = classTest(cls).exec(current);
1457 if (match) {
1458 var after = current.slice(match.index + match[0].length);
1459 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1460 }
1461 };
1462
1463 function removeChildren(e) {
1464 for (var count = e.childNodes.length; count > 0; --count)
1465 { e.removeChild(e.firstChild); }
1466 return e
1467 }
1468
1469 function removeChildrenAndAdd(parent, e) {
1470 return removeChildren(parent).appendChild(e)
1471 }
1472
1473 function elt(tag, content, className, style) {
1474 var e = document.createElement(tag);
1475 if (className) { e.className = className; }
1476 if (style) { e.style.cssText = style; }
1477 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1478 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1479 return e
1480 }
1481 // wrapper for elt, which removes the elt from the accessibility tree
1482 function eltP(tag, content, className, style) {
1483 var e = elt(tag, content, className, style);
1484 e.setAttribute("role", "presentation");
1485 return e
1486 }
1487
1488 var range;
1489 if (document.createRange) { range = function(node, start, end, endNode) {
1490 var r = document.createRange();
1491 r.setEnd(endNode || node, end);
1492 r.setStart(node, start);
1493 return r
1494 }; }
1495 else { range = function(node, start, end) {
1496 var r = document.body.createTextRange();
1497 try { r.moveToElementText(node.parentNode); }
1498 catch(e) { return r }
1499 r.collapse(true);
1500 r.moveEnd("character", end);
1501 r.moveStart("character", start);
1502 return r
1503 }; }
1504
1505 function contains(parent, child) {
1506 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1507 { child = child.parentNode; }
1508 if (parent.contains)
1509 { return parent.contains(child) }
1510 do {
1511 if (child.nodeType == 11) { child = child.host; }
1512 if (child == parent) { return true }
1513 } while (child = child.parentNode)
1514 }
1515
1516 function activeElt() {
1517 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1518 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1519 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1520 var activeElement;
1521 try {
1522 activeElement = document.activeElement;
1523 } catch(e) {
1524 activeElement = document.body || null;
1525 }
1526 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1527 { activeElement = activeElement.shadowRoot.activeElement; }
1528 return activeElement
1529 }
1530
1531 function addClass(node, cls) {
1532 var current = node.className;
1533 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1534 }
1535 function joinClasses(a, b) {
1536 var as = a.split(" ");
1537 for (var i = 0; i < as.length; i++)
1538 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1539 return b
1540 }
1541
1542 var selectInput = function(node) { node.select(); };
1543 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1544 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1545 else if (ie) // Suppress mysterious IE10 errors
1546 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1547
1548 function bind(f) {
1549 var args = Array.prototype.slice.call(arguments, 1);
1550 return function(){return f.apply(null, args)}
1551 }
1552
1553 function copyObj(obj, target, overwrite) {
1554 if (!target) { target = {}; }
1555 for (var prop in obj)
1556 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1557 { target[prop] = obj[prop]; } }
1558 return target
1559 }
1560
1561 // Counts the column offset in a string, taking tabs into account.
1562 // Used mostly to find indentation.
1563 function countColumn(string, end, tabSize, startIndex, startValue) {
1564 if (end == null) {
1565 end = string.search(/[^\s\u00a0]/);
1566 if (end == -1) { end = string.length; }
1567 }
1568 for (var i = startIndex || 0, n = startValue || 0;;) {
1569 var nextTab = string.indexOf("\t", i);
1570 if (nextTab < 0 || nextTab >= end)
1571 { return n + (end - i) }
1572 n += nextTab - i;
1573 n += tabSize - (n % tabSize);
1574 i = nextTab + 1;
1575 }
1576 }
1577
1578 var Delayed = function() {this.id = null;};
1579 Delayed.prototype.set = function (ms, f) {
1580 clearTimeout(this.id);
1581 this.id = setTimeout(f, ms);
1582 };
1583
1584 function indexOf(array, elt) {
1585 for (var i = 0; i < array.length; ++i)
1586 { if (array[i] == elt) { return i } }
1587 return -1
1588 }
1589
1590 // Number of pixels added to scroller and sizer to hide scrollbar
1591 var scrollerGap = 30;
1592
1593 // Returned or thrown by various protocols to signal 'I'm not
1594 // handling this'.
1595 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1596
1597 // Reused option objects for setSelection & friends
1598 var sel_dontScroll = {scroll: false};
1599 var sel_mouse = {origin: "*mouse"};
1600 var sel_move = {origin: "+move"};
1601
1602 // The inverse of countColumn -- find the offset that corresponds to
1603 // a particular column.
1604 function findColumn(string, goal, tabSize) {
1605 for (var pos = 0, col = 0;;) {
1606 var nextTab = string.indexOf("\t", pos);
1607 if (nextTab == -1) { nextTab = string.length; }
1608 var skipped = nextTab - pos;
1609 if (nextTab == string.length || col + skipped >= goal)
1610 { return pos + Math.min(skipped, goal - col) }
1611 col += nextTab - pos;
1612 col += tabSize - (col % tabSize);
1613 pos = nextTab + 1;
1614 if (col >= goal) { return pos }
1615 }
1616 }
1617
1618 var spaceStrs = [""];
1619 function spaceStr(n) {
1620 while (spaceStrs.length <= n)
1621 { spaceStrs.push(lst(spaceStrs) + " "); }
1622 return spaceStrs[n]
1623 }
1624
1625 function lst(arr) { return arr[arr.length-1] }
1626
1627 function map(array, f) {
1628 var out = [];
1629 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1630 return out
1631 }
1632
1633 function insertSorted(array, value, score) {
1634 var pos = 0, priority = score(value);
1635 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1636 array.splice(pos, 0, value);
1637 }
1638
1639 function nothing() {}
1640
1641 function createObj(base, props) {
1642 var inst;
1643 if (Object.create) {
1644 inst = Object.create(base);
1645 } else {
1646 nothing.prototype = base;
1647 inst = new nothing();
1648 }
1649 if (props) { copyObj(props, inst); }
1650 return inst
1651 }
1652
1653 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1654 function isWordCharBasic(ch) {
1655 return /\w/.test(ch) || ch > "\x80" &&
1656 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1657 }
1658 function isWordChar(ch, helper) {
1659 if (!helper) { return isWordCharBasic(ch) }
1660 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1661 return helper.test(ch)
1662 }
1663
1664 function isEmpty(obj) {
1665 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1666 return true
1667 }
1668
1669 // Extending unicode characters. A series of a non-extending char +
1670 // any number of extending chars is treated as a single unit as far
1671 // as editing and measuring is concerned. This is not fully correct,
1672 // since some scripts/fonts/browsers also treat other configurations
1673 // of code points as a group.
1674 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]/;
1675 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1676
1677 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1678 function skipExtendingChars(str, pos, dir) {
1679 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1680 return pos
1681 }
1682
1683 // Returns the value from the range [`from`; `to`] that satisfies
1684 // `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
1685 function findFirst(pred, from, to) {
1686 for (;;) {
1687 if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
1688 var mid = Math.floor((from + to) / 2);
1689 if (pred(mid)) { to = mid; }
1690 else { from = mid; }
1691 }
1692 }
1693
1694 // The display handles the DOM integration, both for input reading
1695 // and content drawing. It holds references to DOM nodes and
1696 // display-related state.
1697
1698 function Display(place, doc, input) {
1699 var d = this;
1700 this.input = input;
1701
1702 // Covers bottom-right square when both scrollbars are present.
1703 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1704 d.scrollbarFiller.setAttribute("cm-not-content", "true");
1705 // Covers bottom of gutter when coverGutterNextToScrollbar is on
1706 // and h scrollbar is present.
1707 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
1708 d.gutterFiller.setAttribute("cm-not-content", "true");
1709 // Will contain the actual code, positioned to cover the viewport.
1710 d.lineDiv = eltP("div", null, "CodeMirror-code");
1711 // Elements are added to these to represent selection and cursors.
1712 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1713 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
1714 // A visibility: hidden element used to find the size of things.
1715 d.measure = elt("div", null, "CodeMirror-measure");
1716 // When lines outside of the viewport are measured, they are drawn in this.
1717 d.lineMeasure = elt("div", null, "CodeMirror-measure");
1718 // Wraps everything that needs to exist inside the vertically-padded coordinate system
1719 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
1720 null, "position: relative; outline: none");
1721 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
1722 // Moved around its parent to cover visible view.
1723 d.mover = elt("div", [lines], null, "position: relative");
1724 // Set to the height of the document, allowing scrolling.
1725 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1726 d.sizerWidth = null;
1727 // Behavior of elts with overflow: auto and padding is
1728 // inconsistent across browsers. This is used to ensure the
1729 // scrollable area is big enough.
1730 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
1731 // Will contain the gutters, if any.
1732 d.gutters = elt("div", null, "CodeMirror-gutters");
1733 d.lineGutter = null;
1734 // Actual scrollable element.
1735 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
1736 d.scroller.setAttribute("tabIndex", "-1");
1737 // The element in which the editor lives.
1738 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
1739
1740 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
1741 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1742 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
1743
1744 if (place) {
1745 if (place.appendChild) { place.appendChild(d.wrapper); }
1746 else { place(d.wrapper); }
1747 }
1748
1749 // Current rendered range (may be bigger than the view window).
1750 d.viewFrom = d.viewTo = doc.first;
1751 d.reportedViewFrom = d.reportedViewTo = doc.first;
1752 // Information about the rendered lines.
1753 d.view = [];
1754 d.renderedView = null;
1755 // Holds info about a single rendered line when it was rendered
1756 // for measurement, while not in view.
1757 d.externalMeasured = null;
1758 // Empty space (in pixels) above the view
1759 d.viewOffset = 0;
1760 d.lastWrapHeight = d.lastWrapWidth = 0;
1761 d.updateLineNumbers = null;
1762
1763 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
1764 d.scrollbarsClipped = false;
1765
1766 // Used to only resize the line number gutter when necessary (when
1767 // the amount of lines crosses a boundary that makes its width change)
1768 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1769 // Set to true when a non-horizontal-scrolling line widget is
1770 // added. As an optimization, line widget aligning is skipped when
1771 // this is false.
1772 d.alignWidgets = false;
1773
1774 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
1775
1776 // Tracks the maximum line length so that the horizontal scrollbar
1777 // can be kept static when scrolling.
1778 d.maxLine = null;
1779 d.maxLineLength = 0;
1780 d.maxLineChanged = false;
1781
1782 // Used for measuring wheel scrolling granularity
1783 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
1784
1785 // True when shift is held down.
1786 d.shift = false;
1787
1788 // Used to track whether anything happened since the context menu
1789 // was opened.
1790 d.selForContextMenu = null;
1791
1792 d.activeTouch = null;
1793
1794 input.init(d);
1795 }
1796
1797 // Find the line object corresponding to the given line number.
1798 function getLine(doc, n) {
1799 n -= doc.first;
1800 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
1801 var chunk = doc;
1802 while (!chunk.lines) {
1803 for (var i = 0;; ++i) {
1804 var child = chunk.children[i], sz = child.chunkSize();
1805 if (n < sz) { chunk = child; break }
1806 n -= sz;
1807 }
1808 }
1809 return chunk.lines[n]
1810 }
1811
1812 // Get the part of a document between two positions, as an array of
1813 // strings.
1814 function getBetween(doc, start, end) {
1815 var out = [], n = start.line;
1816 doc.iter(start.line, end.line + 1, function (line) {
1817 var text = line.text;
1818 if (n == end.line) { text = text.slice(0, end.ch); }
1819 if (n == start.line) { text = text.slice(start.ch); }
1820 out.push(text);
1821 ++n;
1822 });
1823 return out
1824 }
1825 // Get the lines between from and to, as array of strings.
1826 function getLines(doc, from, to) {
1827 var out = [];
1828 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
1829 return out
1830 }
1831
1832 // Update the height of a line, propagating the height change
1833 // upwards to parent nodes.
1834 function updateLineHeight(line, height) {
1835 var diff = height - line.height;
1836 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
1837 }
1838
1839 // Given a line object, find its line number by walking up through
1840 // its parent links.
1841 function lineNo(line) {
1842 if (line.parent == null) { return null }
1843 var cur = line.parent, no = indexOf(cur.lines, line);
1844 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
1845 for (var i = 0;; ++i) {
1846 if (chunk.children[i] == cur) { break }
1847 no += chunk.children[i].chunkSize();
1848 }
1849 }
1850 return no + cur.first
1851 }
1852
1853 // Find the line at the given vertical position, using the height
1854 // information in the document tree.
1855 function lineAtHeight(chunk, h) {
1856 var n = chunk.first;
1857 outer: do {
1858 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
1859 var child = chunk.children[i$1], ch = child.height;
1860 if (h < ch) { chunk = child; continue outer }
1861 h -= ch;
1862 n += child.chunkSize();
1863 }
1864 return n
1865 } while (!chunk.lines)
1866 var i = 0;
1867 for (; i < chunk.lines.length; ++i) {
1868 var line = chunk.lines[i], lh = line.height;
1869 if (h < lh) { break }
1870 h -= lh;
1871 }
1872 return n + i
1873 }
1874
1875 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
1876
1877 function lineNumberFor(options, i) {
1878 return String(options.lineNumberFormatter(i + options.firstLineNumber))
1879 }
1880
1881 // A Pos instance represents a position within the text.
1882 function Pos(line, ch, sticky) {
1883 if ( sticky === void 0 ) sticky = null;
1884
1885 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
1886 this.line = line;
1887 this.ch = ch;
1888 this.sticky = sticky;
1889 }
1890
1891 // Compare two positions, return 0 if they are the same, a negative
1892 // number when a is less, and a positive number otherwise.
1893 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
1894
1895 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
1896
1897 function copyPos(x) {return Pos(x.line, x.ch)}
1898 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
1899 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
1900
1901 // Most of the external API clips given positions to make sure they
1902 // actually exist within the document.
1903 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
1904 function clipPos(doc, pos) {
1905 if (pos.line < doc.first) { return Pos(doc.first, 0) }
1906 var last = doc.first + doc.size - 1;
1907 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
1908 return clipToLen(pos, getLine(doc, pos.line).text.length)
1909 }
1910 function clipToLen(pos, linelen) {
1911 var ch = pos.ch;
1912 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
1913 else if (ch < 0) { return Pos(pos.line, 0) }
1914 else { return pos }
1915 }
1916 function clipPosArray(doc, array) {
1917 var out = [];
1918 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
1919 return out
1920 }
1921
1922 // Optimize some code when these features are not used.
1923 var sawReadOnlySpans = false;
1924 var sawCollapsedSpans = false;
1925
1926 function seeReadOnlySpans() {
1927 sawReadOnlySpans = true;
1928 }
1929
1930 function seeCollapsedSpans() {
1931 sawCollapsedSpans = true;
1932 }
1933
1934 // TEXTMARKER SPANS
1935
1936 function MarkedSpan(marker, from, to) {
1937 this.marker = marker;
1938 this.from = from; this.to = to;
1939 }
1940
1941 // Search an array of spans for a span matching the given marker.
1942 function getMarkedSpanFor(spans, marker) {
1943 if (spans) { for (var i = 0; i < spans.length; ++i) {
1944 var span = spans[i];
1945 if (span.marker == marker) { return span }
1946 } }
1947 }
1948 // Remove a span from an array, returning undefined if no spans are
1949 // left (we don't store arrays for lines without spans).
1950 function removeMarkedSpan(spans, span) {
1951 var r;
1952 for (var i = 0; i < spans.length; ++i)
1953 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
1954 return r
1955 }
1956 // Add a span to a line.
1957 function addMarkedSpan(line, span) {
1958 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
1959 span.marker.attachLine(line);
1960 }
1961
1962 // Used for the algorithm that adjusts markers for a change in the
1963 // document. These functions cut an array of spans at a given
1964 // character position, returning an array of remaining chunks (or
1965 // undefined if nothing remains).
1966 function markedSpansBefore(old, startCh, isInsert) {
1967 var nw;
1968 if (old) { for (var i = 0; i < old.length; ++i) {
1969 var span = old[i], marker = span.marker;
1970 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
1971 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
1972 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));
1973 }
1974 } }
1975 return nw
1976 }
1977 function markedSpansAfter(old, endCh, isInsert) {
1978 var nw;
1979 if (old) { for (var i = 0; i < old.length; ++i) {
1980 var span = old[i], marker = span.marker;
1981 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
1982 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
1983 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
1984 span.to == null ? null : span.to - endCh));
1985 }
1986 } }
1987 return nw
1988 }
1989
1990 // Given a change object, compute the new set of marker spans that
1991 // cover the line in which the change took place. Removes spans
1992 // entirely within the change, reconnects spans belonging to the
1993 // same marker that appear on both sides of the change, and cuts off
1994 // spans partially within the change. Returns an array of span
1995 // arrays with one element for each line in (after) the change.
1996 function stretchSpansOverChange(doc, change) {
1997 if (change.full) { return null }
1998 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
1999 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2000 if (!oldFirst && !oldLast) { return null }
2001
2002 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2003 // Get the spans that 'stick out' on both sides
2004 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2005 var last = markedSpansAfter(oldLast, endCh, isInsert);
2006
2007 // Next, merge those two ends
2008 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2009 if (first) {
2010 // Fix up .to properties of first
2011 for (var i = 0; i < first.length; ++i) {
2012 var span = first[i];
2013 if (span.to == null) {
2014 var found = getMarkedSpanFor(last, span.marker);
2015 if (!found) { span.to = startCh; }
2016 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2017 }
2018 }
2019 }
2020 if (last) {
2021 // Fix up .from in last (or move them into first in case of sameLine)
2022 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2023 var span$1 = last[i$1];
2024 if (span$1.to != null) { span$1.to += offset; }
2025 if (span$1.from == null) {
2026 var found$1 = getMarkedSpanFor(first, span$1.marker);
2027 if (!found$1) {
2028 span$1.from = offset;
2029 if (sameLine) { (first || (first = [])).push(span$1); }
2030 }
2031 } else {
2032 span$1.from += offset;
2033 if (sameLine) { (first || (first = [])).push(span$1); }
2034 }
2035 }
2036 }
2037 // Make sure we didn't create any zero-length spans
2038 if (first) { first = clearEmptySpans(first); }
2039 if (last && last != first) { last = clearEmptySpans(last); }
2040
2041 var newMarkers = [first];
2042 if (!sameLine) {
2043 // Fill gap with whole-line-spans
2044 var gap = change.text.length - 2, gapMarkers;
2045 if (gap > 0 && first)
2046 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2047 { if (first[i$2].to == null)
2048 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2049 for (var i$3 = 0; i$3 < gap; ++i$3)
2050 { newMarkers.push(gapMarkers); }
2051 newMarkers.push(last);
2052 }
2053 return newMarkers
2054 }
2055
2056 // Remove spans that are empty and don't have a clearWhenEmpty
2057 // option of false.
2058 function clearEmptySpans(spans) {
2059 for (var i = 0; i < spans.length; ++i) {
2060 var span = spans[i];
2061 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
2062 { spans.splice(i--, 1); }
2063 }
2064 if (!spans.length) { return null }
2065 return spans
2066 }
2067
2068 // Used to 'clip' out readOnly ranges when making a change.
2069 function removeReadOnlyRanges(doc, from, to) {
2070 var markers = null;
2071 doc.iter(from.line, to.line + 1, function (line) {
2072 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
2073 var mark = line.markedSpans[i].marker;
2074 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
2075 { (markers || (markers = [])).push(mark); }
2076 } }
2077 });
2078 if (!markers) { return null }
2079 var parts = [{from: from, to: to}];
2080 for (var i = 0; i < markers.length; ++i) {
2081 var mk = markers[i], m = mk.find(0);
2082 for (var j = 0; j < parts.length; ++j) {
2083 var p = parts[j];
2084 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
2085 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
2086 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
2087 { newParts.push({from: p.from, to: m.from}); }
2088 if (dto > 0 || !mk.inclusiveRight && !dto)
2089 { newParts.push({from: m.to, to: p.to}); }
2090 parts.splice.apply(parts, newParts);
2091 j += newParts.length - 3;
2092 }
2093 }
2094 return parts
2095 }
2096
2097 // Connect or disconnect spans from a line.
2098 function detachMarkedSpans(line) {
2099 var spans = line.markedSpans;
2100 if (!spans) { return }
2101 for (var i = 0; i < spans.length; ++i)
2102 { spans[i].marker.detachLine(line); }
2103 line.markedSpans = null;
2104 }
2105 function attachMarkedSpans(line, spans) {
2106 if (!spans) { return }
2107 for (var i = 0; i < spans.length; ++i)
2108 { spans[i].marker.attachLine(line); }
2109 line.markedSpans = spans;
2110 }
2111
2112 // Helpers used when computing which overlapping collapsed span
2113 // counts as the larger one.
2114 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
2115 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
2116
2117 // Returns a number indicating which of two overlapping collapsed
2118 // spans is larger (and thus includes the other). Falls back to
2119 // comparing ids when the spans cover exactly the same range.
2120 function compareCollapsedMarkers(a, b) {
2121 var lenDiff = a.lines.length - b.lines.length;
2122 if (lenDiff != 0) { return lenDiff }
2123 var aPos = a.find(), bPos = b.find();
2124 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
2125 if (fromCmp) { return -fromCmp }
2126 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
2127 if (toCmp) { return toCmp }
2128 return b.id - a.id
2129 }
2130
2131 // Find out whether a line ends or starts in a collapsed span. If
2132 // so, return the marker for that span.
2133 function collapsedSpanAtSide(line, start) {
2134 var sps = sawCollapsedSpans && line.markedSpans, found;
2135 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2136 sp = sps[i];
2137 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
2138 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
2139 { found = sp.marker; }
2140 } }
2141 return found
2142 }
2143 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
2144 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
2145
2146 // Test whether there exists a collapsed span that partially
2147 // overlaps (covers the start or end, but not both) of a new span.
2148 // Such overlap is not allowed.
2149 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
2150 var line = getLine(doc, lineNo$$1);
2151 var sps = sawCollapsedSpans && line.markedSpans;
2152 if (sps) { for (var i = 0; i < sps.length; ++i) {
2153 var sp = sps[i];
2154 if (!sp.marker.collapsed) { continue }
2155 var found = sp.marker.find(0);
2156 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
2157 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
2158 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
2159 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
2160 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
2161 { return true }
2162 } }
2163 }
2164
2165 // A visual line is a line as drawn on the screen. Folding, for
2166 // example, can cause multiple logical lines to appear on the same
2167 // visual line. This finds the start of the visual line that the
2168 // given line is part of (usually that is the line itself).
2169 function visualLine(line) {
2170 var merged;
2171 while (merged = collapsedSpanAtStart(line))
2172 { line = merged.find(-1, true).line; }
2173 return line
2174 }
2175
2176 function visualLineEnd(line) {
2177 var merged;
2178 while (merged = collapsedSpanAtEnd(line))
2179 { line = merged.find(1, true).line; }
2180 return line
2181 }
2182
2183 // Returns an array of logical lines that continue the visual line
2184 // started by the argument, or undefined if there are no such lines.
2185 function visualLineContinued(line) {
2186 var merged, lines;
2187 while (merged = collapsedSpanAtEnd(line)) {
2188 line = merged.find(1, true).line
2189 ;(lines || (lines = [])).push(line);
2190 }
2191 return lines
2192 }
2193
2194 // Get the line number of the start of the visual line that the
2195 // given line number is part of.
2196 function visualLineNo(doc, lineN) {
2197 var line = getLine(doc, lineN), vis = visualLine(line);
2198 if (line == vis) { return lineN }
2199 return lineNo(vis)
2200 }
2201
2202 // Get the line number of the start of the next visual line after
2203 // the given line.
2204 function visualLineEndNo(doc, lineN) {
2205 if (lineN > doc.lastLine()) { return lineN }
2206 var line = getLine(doc, lineN), merged;
2207 if (!lineIsHidden(doc, line)) { return lineN }
2208 while (merged = collapsedSpanAtEnd(line))
2209 { line = merged.find(1, true).line; }
2210 return lineNo(line) + 1
2211 }
2212
2213 // Compute whether a line is hidden. Lines count as hidden when they
2214 // are part of a visual line that starts with another line, or when
2215 // they are entirely covered by collapsed, non-widget span.
2216 function lineIsHidden(doc, line) {
2217 var sps = sawCollapsedSpans && line.markedSpans;
2218 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2219 sp = sps[i];
2220 if (!sp.marker.collapsed) { continue }
2221 if (sp.from == null) { return true }
2222 if (sp.marker.widgetNode) { continue }
2223 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
2224 { return true }
2225 } }
2226 }
2227 function lineIsHiddenInner(doc, line, span) {
2228 if (span.to == null) {
2229 var end = span.marker.find(1, true);
2230 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
2231 }
2232 if (span.marker.inclusiveRight && span.to == line.text.length)
2233 { return true }
2234 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
2235 sp = line.markedSpans[i];
2236 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
2237 (sp.to == null || sp.to != span.from) &&
2238 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
2239 lineIsHiddenInner(doc, line, sp)) { return true }
2240 }
2241 }
2242
2243 // Find the height above the given line.
2244 function heightAtLine(lineObj) {
2245 lineObj = visualLine(lineObj);
2246
2247 var h = 0, chunk = lineObj.parent;
2248 for (var i = 0; i < chunk.lines.length; ++i) {
2249 var line = chunk.lines[i];
2250 if (line == lineObj) { break }
2251 else { h += line.height; }
2252 }
2253 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
2254 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
2255 var cur = p.children[i$1];
2256 if (cur == chunk) { break }
2257 else { h += cur.height; }
2258 }
2259 }
2260 return h
2261 }
2262
2263 // Compute the character length of a line, taking into account
2264 // collapsed ranges (see markText) that might hide parts, and join
2265 // other lines onto it.
2266 function lineLength(line) {
2267 if (line.height == 0) { return 0 }
2268 var len = line.text.length, merged, cur = line;
2269 while (merged = collapsedSpanAtStart(cur)) {
2270 var found = merged.find(0, true);
2271 cur = found.from.line;
2272 len += found.from.ch - found.to.ch;
2273 }
2274 cur = line;
2275 while (merged = collapsedSpanAtEnd(cur)) {
2276 var found$1 = merged.find(0, true);
2277 len -= cur.text.length - found$1.from.ch;
2278 cur = found$1.to.line;
2279 len += cur.text.length - found$1.to.ch;
2280 }
2281 return len
2282 }
2283
2284 // Find the longest line in the document.
2285 function findMaxLine(cm) {
2286 var d = cm.display, doc = cm.doc;
2287 d.maxLine = getLine(doc, doc.first);
2288 d.maxLineLength = lineLength(d.maxLine);
2289 d.maxLineChanged = true;
2290 doc.iter(function (line) {
2291 var len = lineLength(line);
2292 if (len > d.maxLineLength) {
2293 d.maxLineLength = len;
2294 d.maxLine = line;
2295 }
2296 });
2297 }
2298
2299 // BIDI HELPERS
2300
2301 function iterateBidiSections(order, from, to, f) {
2302 if (!order) { return f(from, to, "ltr") }
2303 var found = false;
2304 for (var i = 0; i < order.length; ++i) {
2305 var part = order[i];
2306 if (part.from < to && part.to > from || from == to && part.to == from) {
2307 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
2308 found = true;
2309 }
2310 }
2311 if (!found) { f(from, to, "ltr"); }
2312 }
2313
2314 var bidiOther = null;
2315 function getBidiPartAt(order, ch, sticky) {
2316 var found;
2317 bidiOther = null;
2318 for (var i = 0; i < order.length; ++i) {
2319 var cur = order[i];
2320 if (cur.from < ch && cur.to > ch) { return i }
2321 if (cur.to == ch) {
2322 if (cur.from != cur.to && sticky == "before") { found = i; }
2323 else { bidiOther = i; }
2324 }
2325 if (cur.from == ch) {
2326 if (cur.from != cur.to && sticky != "before") { found = i; }
2327 else { bidiOther = i; }
2328 }
2329 }
2330 return found != null ? found : bidiOther
2331 }
2332
2333 // Bidirectional ordering algorithm
2334 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
2335 // that this (partially) implements.
2336
2337 // One-char codes used for character types:
2338 // L (L): Left-to-Right
2339 // R (R): Right-to-Left
2340 // r (AL): Right-to-Left Arabic
2341 // 1 (EN): European Number
2342 // + (ES): European Number Separator
2343 // % (ET): European Number Terminator
2344 // n (AN): Arabic Number
2345 // , (CS): Common Number Separator
2346 // m (NSM): Non-Spacing Mark
2347 // b (BN): Boundary Neutral
2348 // s (B): Paragraph Separator
2349 // t (S): Segment Separator
2350 // w (WS): Whitespace
2351 // N (ON): Other Neutrals
2352
2353 // Returns null if characters are ordered as they appear
2354 // (left-to-right), or an array of sections ({from, to, level}
2355 // objects) in the order in which they occur visually.
2356 var bidiOrdering = (function() {
2357 // Character types for codepoints 0 to 0xff
2358 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
2359 // Character types for codepoints 0x600 to 0x6f9
2360 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
2361 function charType(code) {
2362 if (code <= 0xf7) { return lowTypes.charAt(code) }
2363 else if (0x590 <= code && code <= 0x5f4) { return "R" }
2364 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
2365 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
2366 else if (0x2000 <= code && code <= 0x200b) { return "w" }
2367 else if (code == 0x200c) { return "b" }
2368 else { return "L" }
2369 }
2370
2371 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2372 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
2373
2374 function BidiSpan(level, from, to) {
2375 this.level = level;
2376 this.from = from; this.to = to;
2377 }
2378
2379 return function(str, direction) {
2380 var outerType = direction == "ltr" ? "L" : "R";
2381
2382 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
2383 var len = str.length, types = [];
2384 for (var i = 0; i < len; ++i)
2385 { types.push(charType(str.charCodeAt(i))); }
2386
2387 // W1. Examine each non-spacing mark (NSM) in the level run, and
2388 // change the type of the NSM to the type of the previous
2389 // character. If the NSM is at the start of the level run, it will
2390 // get the type of sor.
2391 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2392 var type = types[i$1];
2393 if (type == "m") { types[i$1] = prev; }
2394 else { prev = type; }
2395 }
2396
2397 // W2. Search backwards from each instance of a European number
2398 // until the first strong type (R, L, AL, or sor) is found. If an
2399 // AL is found, change the type of the European number to Arabic
2400 // number.
2401 // W3. Change all ALs to R.
2402 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2403 var type$1 = types[i$2];
2404 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2405 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2406 }
2407
2408 // W4. A single European separator between two European numbers
2409 // changes to a European number. A single common separator between
2410 // two numbers of the same type changes to that type.
2411 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2412 var type$2 = types[i$3];
2413 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2414 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2415 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2416 prev$1 = type$2;
2417 }
2418
2419 // W5. A sequence of European terminators adjacent to European
2420 // numbers changes to all European numbers.
2421 // W6. Otherwise, separators and terminators change to Other
2422 // Neutral.
2423 for (var i$4 = 0; i$4 < len; ++i$4) {
2424 var type$3 = types[i$4];
2425 if (type$3 == ",") { types[i$4] = "N"; }
2426 else if (type$3 == "%") {
2427 var end = (void 0);
2428 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2429 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2430 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2431 i$4 = end - 1;
2432 }
2433 }
2434
2435 // W7. Search backwards from each instance of a European number
2436 // until the first strong type (R, L, or sor) is found. If an L is
2437 // found, then change the type of the European number to L.
2438 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2439 var type$4 = types[i$5];
2440 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2441 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2442 }
2443
2444 // N1. A sequence of neutrals takes the direction of the
2445 // surrounding strong text if the text on both sides has the same
2446 // direction. European and Arabic numbers act as if they were R in
2447 // terms of their influence on neutrals. Start-of-level-run (sor)
2448 // and end-of-level-run (eor) are used at level run boundaries.
2449 // N2. Any remaining neutrals take the embedding direction.
2450 for (var i$6 = 0; i$6 < len; ++i$6) {
2451 if (isNeutral.test(types[i$6])) {
2452 var end$1 = (void 0);
2453 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2454 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2455 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2456 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2457 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2458 i$6 = end$1 - 1;
2459 }
2460 }
2461
2462 // Here we depart from the documented algorithm, in order to avoid
2463 // building up an actual levels array. Since there are only three
2464 // levels (0, 1, 2) in an implementation that doesn't take
2465 // explicit embedding into account, we can build up the order on
2466 // the fly, without following the level-based algorithm.
2467 var order = [], m;
2468 for (var i$7 = 0; i$7 < len;) {
2469 if (countsAsLeft.test(types[i$7])) {
2470 var start = i$7;
2471 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2472 order.push(new BidiSpan(0, start, i$7));
2473 } else {
2474 var pos = i$7, at = order.length;
2475 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2476 for (var j$2 = pos; j$2 < i$7;) {
2477 if (countsAsNum.test(types[j$2])) {
2478 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2479 var nstart = j$2;
2480 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2481 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2482 pos = j$2;
2483 } else { ++j$2; }
2484 }
2485 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2486 }
2487 }
2488 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2489 order[0].from = m[0].length;
2490 order.unshift(new BidiSpan(0, 0, m[0].length));
2491 }
2492 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2493 lst(order).to -= m[0].length;
2494 order.push(new BidiSpan(0, len - m[0].length, len));
2495 }
2496
2497 return direction == "rtl" ? order.reverse() : order
2498 }
2499 })();
2500
2501 // Get the bidi ordering for the given line (and cache it). Returns
2502 // false for lines that are fully left-to-right, and an array of
2503 // BidiSpan objects otherwise.
2504 function getOrder(line, direction) {
2505 var order = line.order;
2506 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2507 return order
2508 }
2509
2510 function moveCharLogically(line, ch, dir) {
2511 var target = skipExtendingChars(line.text, ch + dir, dir);
2512 return target < 0 || target > line.text.length ? null : target
2513 }
2514
2515 function moveLogically(line, start, dir) {
2516 var ch = moveCharLogically(line, start.ch, dir);
2517 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
2518 }
2519
2520 function endOfLine(visually, cm, lineObj, lineNo, dir) {
2521 if (visually) {
2522 var order = getOrder(lineObj, cm.doc.direction);
2523 if (order) {
2524 var part = dir < 0 ? lst(order) : order[0];
2525 var moveInStorageOrder = (dir < 0) == (part.level == 1);
2526 var sticky = moveInStorageOrder ? "after" : "before";
2527 var ch;
2528 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
2529 // it could be that the last bidi part is not on the last visual line,
2530 // since visual lines contain content order-consecutive chunks.
2531 // Thus, in rtl, we are looking for the first (content-order) character
2532 // in the rtl chunk that is on the last line (that is, the same line
2533 // as the last (content-order) character).
2534 if (part.level > 0) {
2535 var prep = prepareMeasureForLine(cm, lineObj);
2536 ch = dir < 0 ? lineObj.text.length - 1 : 0;
2537 var targetTop = measureCharPrepared(cm, prep, ch).top;
2538 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
2539 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
2540 } else { ch = dir < 0 ? part.to : part.from; }
2541 return new Pos(lineNo, ch, sticky)
2542 }
2543 }
2544 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
2545 }
2546
2547 function moveVisually(cm, line, start, dir) {
2548 var bidi = getOrder(line, cm.doc.direction);
2549 if (!bidi) { return moveLogically(line, start, dir) }
2550 if (start.ch >= line.text.length) {
2551 start.ch = line.text.length;
2552 start.sticky = "before";
2553 } else if (start.ch <= 0) {
2554 start.ch = 0;
2555 start.sticky = "after";
2556 }
2557 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
2558 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
2559 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
2560 // nothing interesting happens.
2561 return moveLogically(line, start, dir)
2562 }
2563
2564 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
2565 var prep;
2566 var getWrappedLineExtent = function (ch) {
2567 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
2568 prep = prep || prepareMeasureForLine(cm, line);
2569 return wrappedLineExtentChar(cm, line, prep, ch)
2570 };
2571 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
2572
2573 if (cm.doc.direction == "rtl" || part.level == 1) {
2574 var moveInStorageOrder = (part.level == 1) == (dir < 0);
2575 var ch = mv(start, moveInStorageOrder ? 1 : -1);
2576 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
2577 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
2578 var sticky = moveInStorageOrder ? "before" : "after";
2579 return new Pos(start.line, ch, sticky)
2580 }
2581 }
2582
2583 // Case 3: Could not move within this bidi part in this visual line, so leave
2584 // the current bidi part
2585
2586 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
2587 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
2588 ? new Pos(start.line, mv(ch, 1), "before")
2589 : new Pos(start.line, ch, "after"); };
2590
2591 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
2592 var part = bidi[partPos];
2593 var moveInStorageOrder = (dir > 0) == (part.level != 1);
2594 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
2595 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
2596 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
2597 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
2598 }
2599 };
2600
2601 // Case 3a: Look for other bidi parts on the same visual line
2602 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
2603 if (res) { return res }
2604
2605 // Case 3b: Look for other bidi parts on the next visual line
2606 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
2607 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
2608 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
2609 if (res) { return res }
2610 }
2611
2612 // Case 4: Nowhere to move
2613 return null
2614 }
2615
2616 // EVENT HANDLING
2617
2618 // Lightweight event framework. on/off also work on DOM nodes,
2619 // registering native DOM handlers.
2620
2621 var noHandlers = [];
2622
2623 var on = function(emitter, type, f) {
2624 if (emitter.addEventListener) {
2625 emitter.addEventListener(type, f, false);
2626 } else if (emitter.attachEvent) {
2627 emitter.attachEvent("on" + type, f);
2628 } else {
2629 var map$$1 = emitter._handlers || (emitter._handlers = {});
2630 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2631 }
2632 };
2633
2634 function getHandlers(emitter, type) {
2635 return emitter._handlers && emitter._handlers[type] || noHandlers
2636 }
2637
2638 function off(emitter, type, f) {
2639 if (emitter.removeEventListener) {
2640 emitter.removeEventListener(type, f, false);
2641 } else if (emitter.detachEvent) {
2642 emitter.detachEvent("on" + type, f);
2643 } else {
2644 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2645 if (arr) {
2646 var index = indexOf(arr, f);
2647 if (index > -1)
2648 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2649 }
2650 }
2651 }
2652
2653 function signal(emitter, type /*, values...*/) {
2654 var handlers = getHandlers(emitter, type);
2655 if (!handlers.length) { return }
2656 var args = Array.prototype.slice.call(arguments, 2);
2657 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2658 }
2659
2660 // The DOM events that CodeMirror handles can be overridden by
2661 // registering a (non-DOM) handler on the editor for the event name,
2662 // and preventDefault-ing the event in that handler.
2663 function signalDOMEvent(cm, e, override) {
2664 if (typeof e == "string")
2665 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2666 signal(cm, override || e.type, cm, e);
2667 return e_defaultPrevented(e) || e.codemirrorIgnore
2668 }
2669
2670 function signalCursorActivity(cm) {
2671 var arr = cm._handlers && cm._handlers.cursorActivity;
2672 if (!arr) { return }
2673 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2674 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2675 { set.push(arr[i]); } }
2676 }
2677
2678 function hasHandler(emitter, type) {
2679 return getHandlers(emitter, type).length > 0
2680 }
2681
2682 // Add on and off methods to a constructor's prototype, to make
2683 // registering events on such objects more convenient.
2684 function eventMixin(ctor) {
2685 ctor.prototype.on = function(type, f) {on(this, type, f);};
2686 ctor.prototype.off = function(type, f) {off(this, type, f);};
2687 }
2688
2689 // Due to the fact that we still support jurassic IE versions, some
2690 // compatibility wrappers are needed.
2691
2692 function e_preventDefault(e) {
2693 if (e.preventDefault) { e.preventDefault(); }
2694 else { e.returnValue = false; }
2695 }
2696 function e_stopPropagation(e) {
2697 if (e.stopPropagation) { e.stopPropagation(); }
2698 else { e.cancelBubble = true; }
2699 }
2700 function e_defaultPrevented(e) {
2701 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2702 }
2703 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2704
2705 function e_target(e) {return e.target || e.srcElement}
2706 function e_button(e) {
2707 var b = e.which;
2708 if (b == null) {
2709 if (e.button & 1) { b = 1; }
2710 else if (e.button & 2) { b = 3; }
2711 else if (e.button & 4) { b = 2; }
2712 }
2713 if (mac && e.ctrlKey && b == 1) { b = 3; }
2714 return b
2715 }
2716
2717 // Detect drag-and-drop
2718 var dragAndDrop = function() {
2719 // There is *some* kind of drag-and-drop support in IE6-8, but I
2720 // couldn't get it to work yet.
2721 if (ie && ie_version < 9) { return false }
2722 var div = elt('div');
2723 return "draggable" in div || "dragDrop" in div
2724 }();
2725
2726 var zwspSupported;
2727 function zeroWidthElement(measure) {
2728 if (zwspSupported == null) {
2729 var test = elt("span", "\u200b");
2730 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2731 if (measure.firstChild.offsetHeight != 0)
2732 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2733 }
2734 var node = zwspSupported ? elt("span", "\u200b") :
2735 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2736 node.setAttribute("cm-text", "");
2737 return node
2738 }
2739
2740 // Feature-detect IE's crummy client rect reporting for bidi text
2741 var badBidiRects;
2742 function hasBadBidiRects(measure) {
2743 if (badBidiRects != null) { return badBidiRects }
2744 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2745 var r0 = range(txt, 0, 1).getBoundingClientRect();
2746 var r1 = range(txt, 1, 2).getBoundingClientRect();
2747 removeChildren(measure);
2748 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2749 return badBidiRects = (r1.right - r0.right < 3)
2750 }
2751
2752 // See if "".split is the broken IE version, if so, provide an
2753 // alternative way to split lines.
2754 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2755 var pos = 0, result = [], l = string.length;
2756 while (pos <= l) {
2757 var nl = string.indexOf("\n", pos);
2758 if (nl == -1) { nl = string.length; }
2759 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2760 var rt = line.indexOf("\r");
2761 if (rt != -1) {
2762 result.push(line.slice(0, rt));
2763 pos += rt + 1;
2764 } else {
2765 result.push(line);
2766 pos = nl + 1;
2767 }
2768 }
2769 return result
2770 } : function (string) { return string.split(/\r\n?|\n/); };
2771
2772 var hasSelection = window.getSelection ? function (te) {
2773 try { return te.selectionStart != te.selectionEnd }
2774 catch(e) { return false }
2775 } : function (te) {
2776 var range$$1;
2777 try {range$$1 = te.ownerDocument.selection.createRange();}
2778 catch(e) {}
2779 if (!range$$1 || range$$1.parentElement() != te) { return false }
2780 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2781 };
2782
2783 var hasCopyEvent = (function () {
2784 var e = elt("div");
2785 if ("oncopy" in e) { return true }
2786 e.setAttribute("oncopy", "return;");
2787 return typeof e.oncopy == "function"
2788 })();
2789
2790 var badZoomedRects = null;
2791 function hasBadZoomedRects(measure) {
2792 if (badZoomedRects != null) { return badZoomedRects }
2793 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2794 var normal = node.getBoundingClientRect();
2795 var fromRange = range(node, 0, 1).getBoundingClientRect();
2796 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2797 }
2798
2799 // Known modes, by name and by MIME
2800 var modes = {};
2801 var mimeModes = {};
2802
2803 // Extra arguments are stored as the mode's dependencies, which is
2804 // used by (legacy) mechanisms like loadmode.js to automatically
2805 // load a mode. (Preferred mechanism is the require/define calls.)
2806 function defineMode(name, mode) {
2807 if (arguments.length > 2)
2808 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2809 modes[name] = mode;
2810 }
2811
2812 function defineMIME(mime, spec) {
2813 mimeModes[mime] = spec;
2814 }
2815
2816 // Given a MIME type, a {name, ...options} config object, or a name
2817 // string, return a mode config object.
2818 function resolveMode(spec) {
2819 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2820 spec = mimeModes[spec];
2821 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2822 var found = mimeModes[spec.name];
2823 if (typeof found == "string") { found = {name: found}; }
2824 spec = createObj(found, spec);
2825 spec.name = found.name;
2826 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2827 return resolveMode("application/xml")
2828 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2829 return resolveMode("application/json")
2830 }
2831 if (typeof spec == "string") { return {name: spec} }
2832 else { return spec || {name: "null"} }
2833 }
2834
2835 // Given a mode spec (anything that resolveMode accepts), find and
2836 // initialize an actual mode object.
2837 function getMode(options, spec) {
2838 spec = resolveMode(spec);
2839 var mfactory = modes[spec.name];
2840 if (!mfactory) { return getMode(options, "text/plain") }
2841 var modeObj = mfactory(options, spec);
2842 if (modeExtensions.hasOwnProperty(spec.name)) {
2843 var exts = modeExtensions[spec.name];
2844 for (var prop in exts) {
2845 if (!exts.hasOwnProperty(prop)) { continue }
2846 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2847 modeObj[prop] = exts[prop];
2848 }
2849 }
2850 modeObj.name = spec.name;
2851 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2852 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2853 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2854
2855 return modeObj
2856 }
2857
2858 // This can be used to attach properties to mode objects from
2859 // outside the actual mode definition.
2860 var modeExtensions = {};
2861 function extendMode(mode, properties) {
2862 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2863 copyObj(properties, exts);
2864 }
2865
2866 function copyState(mode, state) {
2867 if (state === true) { return state }
2868 if (mode.copyState) { return mode.copyState(state) }
2869 var nstate = {};
2870 for (var n in state) {
2871 var val = state[n];
2872 if (val instanceof Array) { val = val.concat([]); }
2873 nstate[n] = val;
2874 }
2875 return nstate
2876 }
2877
2878 // Given a mode and a state (for that mode), find the inner mode and
2879 // state at the position that the state refers to.
2880 function innerMode(mode, state) {
2881 var info;
2882 while (mode.innerMode) {
2883 info = mode.innerMode(state);
2884 if (!info || info.mode == mode) { break }
2885 state = info.state;
2886 mode = info.mode;
2887 }
2888 return info || {mode: mode, state: state}
2889 }
2890
2891 function startState(mode, a1, a2) {
2892 return mode.startState ? mode.startState(a1, a2) : true
2893 }
2894
2895 // STRING STREAM
2896
2897 // Fed to the mode parsers, provides helper functions to make
2898 // parsers more succinct.
2899
2900 var StringStream = function(string, tabSize, lineOracle) {
2901 this.pos = this.start = 0;
2902 this.string = string;
2903 this.tabSize = tabSize || 8;
2904 this.lastColumnPos = this.lastColumnValue = 0;
2905 this.lineStart = 0;
2906 this.lineOracle = lineOracle;
2907 };
2908
2909 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
2910 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
2911 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
2912 StringStream.prototype.next = function () {
2913 if (this.pos < this.string.length)
2914 { return this.string.charAt(this.pos++) }
2915 };
2916 StringStream.prototype.eat = function (match) {
2917 var ch = this.string.charAt(this.pos);
2918 var ok;
2919 if (typeof match == "string") { ok = ch == match; }
2920 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
2921 if (ok) {++this.pos; return ch}
2922 };
2923 StringStream.prototype.eatWhile = function (match) {
2924 var start = this.pos;
2925 while (this.eat(match)){}
2926 return this.pos > start
2927 };
2928 StringStream.prototype.eatSpace = function () {
2929 var this$1 = this;
2930
2931 var start = this.pos;
2932 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
2933 return this.pos > start
2934 };
2935 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
2936 StringStream.prototype.skipTo = function (ch) {
2937 var found = this.string.indexOf(ch, this.pos);
2938 if (found > -1) {this.pos = found; return true}
2939 };
2940 StringStream.prototype.backUp = function (n) {this.pos -= n;};
2941 StringStream.prototype.column = function () {
2942 if (this.lastColumnPos < this.start) {
2943 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
2944 this.lastColumnPos = this.start;
2945 }
2946 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2947 };
2948 StringStream.prototype.indentation = function () {
2949 return countColumn(this.string, null, this.tabSize) -
2950 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2951 };
2952 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
2953 if (typeof pattern == "string") {
2954 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
2955 var substr = this.string.substr(this.pos, pattern.length);
2956 if (cased(substr) == cased(pattern)) {
2957 if (consume !== false) { this.pos += pattern.length; }
2958 return true
2959 }
2960 } else {
2961 var match = this.string.slice(this.pos).match(pattern);
2962 if (match && match.index > 0) { return null }
2963 if (match && consume !== false) { this.pos += match[0].length; }
2964 return match
2965 }
2966 };
2967 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
2968 StringStream.prototype.hideFirstChars = function (n, inner) {
2969 this.lineStart += n;
2970 try { return inner() }
2971 finally { this.lineStart -= n; }
2972 };
2973 StringStream.prototype.lookAhead = function (n) {
2974 var oracle = this.lineOracle;
2975 return oracle && oracle.lookAhead(n)
2976 };
2977
2978 var SavedContext = function(state, lookAhead) {
2979 this.state = state;
2980 this.lookAhead = lookAhead;
2981 };
2982
2983 var Context = function(doc, state, line, lookAhead) {
2984 this.state = state;
2985 this.doc = doc;
2986 this.line = line;
2987 this.maxLookAhead = lookAhead || 0;
2988 };
2989
2990 Context.prototype.lookAhead = function (n) {
2991 var line = this.doc.getLine(this.line + n);
2992 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
2993 return line
2994 };
2995
2996 Context.prototype.nextLine = function () {
2997 this.line++;
2998 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
2999 };
3000
3001 Context.fromSaved = function (doc, saved, line) {
3002 if (saved instanceof SavedContext)
3003 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
3004 else
3005 { return new Context(doc, copyState(doc.mode, saved), line) }
3006 };
3007
3008 Context.prototype.save = function (copy) {
3009 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
3010 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
3011 };
3012
3013
3014 // Compute a style array (an array starting with a mode generation
3015 // -- for invalidation -- followed by pairs of end positions and
3016 // style strings), which is used to highlight the tokens on the
3017 // line.
3018 function highlightLine(cm, line, context, forceToEnd) {
3019 // A styles array always starts with a number identifying the
3020 // mode/overlays that it is based on (for easy invalidation).
3021 var st = [cm.state.modeGen], lineClasses = {};
3022 // Compute the base array of styles
3023 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
3024 lineClasses, forceToEnd);
3025 var state = context.state;
3026
3027 // Run overlays, adjust style array.
3028 var loop = function ( o ) {
3029 var overlay = cm.state.overlays[o], i = 1, at = 0;
3030 context.state = true;
3031 runMode(cm, line.text, overlay.mode, context, function (end, style) {
3032 var start = i;
3033 // Ensure there's a token end at the current position, and that i points at it
3034 while (at < end) {
3035 var i_end = st[i];
3036 if (i_end > end)
3037 { st.splice(i, 1, end, st[i+1], i_end); }
3038 i += 2;
3039 at = Math.min(end, i_end);
3040 }
3041 if (!style) { return }
3042 if (overlay.opaque) {
3043 st.splice(start, i - start, end, "overlay " + style);
3044 i = start + 2;
3045 } else {
3046 for (; start < i; start += 2) {
3047 var cur = st[start+1];
3048 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
3049 }
3050 }
3051 }, lineClasses);
3052 };
3053
3054 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
3055 context.state = state;
3056
3057 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
3058 }
3059
3060 function getLineStyles(cm, line, updateFrontier) {
3061 if (!line.styles || line.styles[0] != cm.state.modeGen) {
3062 var context = getContextBefore(cm, lineNo(line));
3063 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
3064 var result = highlightLine(cm, line, context);
3065 if (resetState) { context.state = resetState; }
3066 line.stateAfter = context.save(!resetState);
3067 line.styles = result.styles;
3068 if (result.classes) { line.styleClasses = result.classes; }
3069 else if (line.styleClasses) { line.styleClasses = null; }
3070 if (updateFrontier === cm.doc.highlightFrontier)
3071 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
3072 }
3073 return line.styles
3074 }
3075
3076 function getContextBefore(cm, n, precise) {
3077 var doc = cm.doc, display = cm.display;
3078 if (!doc.mode.startState) { return new Context(doc, true, n) }
3079 var start = findStartLine(cm, n, precise);
3080 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
3081 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
3082
3083 doc.iter(start, n, function (line) {
3084 processLine(cm, line.text, context);
3085 var pos = context.line;
3086 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
3087 context.nextLine();
3088 });
3089 if (precise) { doc.modeFrontier = context.line; }
3090 return context
3091 }
3092
3093 // Lightweight form of highlight -- proceed over this line and
3094 // update state, but don't save a style array. Used for lines that
3095 // aren't currently visible.
3096 function processLine(cm, text, context, startAt) {
3097 var mode = cm.doc.mode;
3098 var stream = new StringStream(text, cm.options.tabSize, context);
3099 stream.start = stream.pos = startAt || 0;
3100 if (text == "") { callBlankLine(mode, context.state); }
3101 while (!stream.eol()) {
3102 readToken(mode, stream, context.state);
3103 stream.start = stream.pos;
3104 }
3105 }
3106
3107 function callBlankLine(mode, state) {
3108 if (mode.blankLine) { return mode.blankLine(state) }
3109 if (!mode.innerMode) { return }
3110 var inner = innerMode(mode, state);
3111 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
3112 }
3113
3114 function readToken(mode, stream, state, inner) {
3115 for (var i = 0; i < 10; i++) {
3116 if (inner) { inner[0] = innerMode(mode, state).mode; }
3117 var style = mode.token(stream, state);
3118 if (stream.pos > stream.start) { return style }
3119 }
3120 throw new Error("Mode " + mode.name + " failed to advance stream.")
3121 }
3122
3123 var Token = function(stream, type, state) {
3124 this.start = stream.start; this.end = stream.pos;
3125 this.string = stream.current();
3126 this.type = type || null;
3127 this.state = state;
3128 };
3129
3130 // Utility for getTokenAt and getLineTokens
3131 function takeToken(cm, pos, precise, asArray) {
3132 var doc = cm.doc, mode = doc.mode, style;
3133 pos = clipPos(doc, pos);
3134 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
3135 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
3136 if (asArray) { tokens = []; }
3137 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
3138 stream.start = stream.pos;
3139 style = readToken(mode, stream, context.state);
3140 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
3141 }
3142 return asArray ? tokens : new Token(stream, style, context.state)
3143 }
3144
3145 function extractLineClasses(type, output) {
3146 if (type) { for (;;) {
3147 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
3148 if (!lineClass) { break }
3149 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
3150 var prop = lineClass[1] ? "bgClass" : "textClass";
3151 if (output[prop] == null)
3152 { output[prop] = lineClass[2]; }
3153 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
3154 { output[prop] += " " + lineClass[2]; }
3155 } }
3156 return type
3157 }
3158
3159 // Run the given mode's parser over a line, calling f for each token.
3160 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
3161 var flattenSpans = mode.flattenSpans;
3162 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
3163 var curStart = 0, curStyle = null;
3164 var stream = new StringStream(text, cm.options.tabSize, context), style;
3165 var inner = cm.options.addModeClass && [null];
3166 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
3167 while (!stream.eol()) {
3168 if (stream.pos > cm.options.maxHighlightLength) {
3169 flattenSpans = false;
3170 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
3171 stream.pos = text.length;
3172 style = null;
3173 } else {
3174 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
3175 }
3176 if (inner) {
3177 var mName = inner[0].name;
3178 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
3179 }
3180 if (!flattenSpans || curStyle != style) {
3181 while (curStart < stream.start) {
3182 curStart = Math.min(stream.start, curStart + 5000);
3183 f(curStart, curStyle);
3184 }
3185 curStyle = style;
3186 }
3187 stream.start = stream.pos;
3188 }
3189 while (curStart < stream.pos) {
3190 // Webkit seems to refuse to render text nodes longer than 57444
3191 // characters, and returns inaccurate measurements in nodes
3192 // starting around 5000 chars.
3193 var pos = Math.min(stream.pos, curStart + 5000);
3194 f(pos, curStyle);
3195 curStart = pos;
3196 }
3197 }
3198
3199 // Finds the line to start with when starting a parse. Tries to
3200 // find a line with a stateAfter, so that it can start with a
3201 // valid state. If that fails, it returns the line with the
3202 // smallest indentation, which tends to need the least context to
3203 // parse correctly.
3204 function findStartLine(cm, n, precise) {
3205 var minindent, minline, doc = cm.doc;
3206 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
3207 for (var search = n; search > lim; --search) {
3208 if (search <= doc.first) { return doc.first }
3209 var line = getLine(doc, search - 1), after = line.stateAfter;
3210 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
3211 { return search }
3212 var indented = countColumn(line.text, null, cm.options.tabSize);
3213 if (minline == null || minindent > indented) {
3214 minline = search - 1;
3215 minindent = indented;
3216 }
3217 }
3218 return minline
3219 }
3220
3221 function retreatFrontier(doc, n) {
3222 doc.modeFrontier = Math.min(doc.modeFrontier, n);
3223 if (doc.highlightFrontier < n - 10) { return }
3224 var start = doc.first;
3225 for (var line = n - 1; line > start; line--) {
3226 var saved = getLine(doc, line).stateAfter;
3227 // change is on 3
3228 // state on line 1 looked ahead 2 -- so saw 3
3229 // test 1 + 2 < 3 should cover this
3230 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
3231 start = line + 1;
3232 break
3233 }
3234 }
3235 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
3236 }
3237
3238 // LINE DATA STRUCTURE
3239
3240 // Line objects. These hold state related to a line, including
3241 // highlighting info (the styles array).
3242 var Line = function(text, markedSpans, estimateHeight) {
3243 this.text = text;
3244 attachMarkedSpans(this, markedSpans);
3245 this.height = estimateHeight ? estimateHeight(this) : 1;
3246 };
3247
3248 Line.prototype.lineNo = function () { return lineNo(this) };
3249 eventMixin(Line);
3250
3251 // Change the content (text, markers) of a line. Automatically
3252 // invalidates cached information and tries to re-estimate the
3253 // line's height.
3254 function updateLine(line, text, markedSpans, estimateHeight) {
3255 line.text = text;
3256 if (line.stateAfter) { line.stateAfter = null; }
3257 if (line.styles) { line.styles = null; }
3258 if (line.order != null) { line.order = null; }
3259 detachMarkedSpans(line);
3260 attachMarkedSpans(line, markedSpans);
3261 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3262 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3263 }
3264
3265 // Detach a line from the document tree and its markers.
3266 function cleanUpLine(line) {
3267 line.parent = null;
3268 detachMarkedSpans(line);
3269 }
3270
3271 // Convert a style as returned by a mode (either null, or a string
3272 // containing one or more styles) to a CSS style. This is cached,
3273 // and also looks for line-wide styles.
3274 var styleToClassCache = {};
3275 var styleToClassCacheWithMode = {};
3276 function interpretTokenStyle(style, options) {
3277 if (!style || /^\s*$/.test(style)) { return null }
3278 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3279 return cache[style] ||
3280 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3281 }
3282
3283 // Render the DOM representation of the text of a line. Also builds
3284 // up a 'line map', which points at the DOM nodes that represent
3285 // specific stretches of text, and is used by the measuring code.
3286 // The returned object contains the DOM node, this map, and
3287 // information about line-wide styles that were set by the mode.
3288 function buildLineContent(cm, lineView) {
3289 // The padding-right forces the element to have a 'border', which
3290 // is needed on Webkit to be able to get line-level bounding
3291 // rectangles for it (in measureChar).
3292 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3293 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3294 col: 0, pos: 0, cm: cm,
3295 trailingSpace: false,
3296 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
3297 lineView.measure = {};
3298
3299 // Iterate over the logical lines that make up this visual line.
3300 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3301 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3302 builder.pos = 0;
3303 builder.addToken = buildToken;
3304 // Optionally wire in some hacks into the token-rendering
3305 // algorithm, to deal with browser quirks.
3306 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3307 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3308 builder.map = [];
3309 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3310 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3311 if (line.styleClasses) {
3312 if (line.styleClasses.bgClass)
3313 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3314 if (line.styleClasses.textClass)
3315 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3316 }
3317
3318 // Ensure at least a single node is present, for measuring.
3319 if (builder.map.length == 0)
3320 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3321
3322 // Store the map and a cache object for the current logical line
3323 if (i == 0) {
3324 lineView.measure.map = builder.map;
3325 lineView.measure.cache = {};
3326 } else {
3327 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3328 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3329 }
3330 }
3331
3332 // See issue #2901
3333 if (webkit) {
3334 var last = builder.content.lastChild;
3335 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3336 { builder.content.className = "cm-tab-wrap-hack"; }
3337 }
3338
3339 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3340 if (builder.pre.className)
3341 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3342
3343 return builder
3344 }
3345
3346 function defaultSpecialCharPlaceholder(ch) {
3347 var token = elt("span", "\u2022", "cm-invalidchar");
3348 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3349 token.setAttribute("aria-label", token.title);
3350 return token
3351 }
3352
3353 // Build up the DOM representation for a single token, and add it to
3354 // the line map. Takes care to render special characters separately.
3355 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
3356 if (!text) { return }
3357 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3358 var special = builder.cm.state.specialChars, mustWrap = false;
3359 var content;
3360 if (!special.test(text)) {
3361 builder.col += text.length;
3362 content = document.createTextNode(displayText);
3363 builder.map.push(builder.pos, builder.pos + text.length, content);
3364 if (ie && ie_version < 9) { mustWrap = true; }
3365 builder.pos += text.length;
3366 } else {
3367 content = document.createDocumentFragment();
3368 var pos = 0;
3369 while (true) {
3370 special.lastIndex = pos;
3371 var m = special.exec(text);
3372 var skipped = m ? m.index - pos : text.length - pos;
3373 if (skipped) {
3374 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3375 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3376 else { content.appendChild(txt); }
3377 builder.map.push(builder.pos, builder.pos + skipped, txt);
3378 builder.col += skipped;
3379 builder.pos += skipped;
3380 }
3381 if (!m) { break }
3382 pos += skipped + 1;
3383 var txt$1 = (void 0);
3384 if (m[0] == "\t") {
3385 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3386 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3387 txt$1.setAttribute("role", "presentation");
3388 txt$1.setAttribute("cm-text", "\t");
3389 builder.col += tabWidth;
3390 } else if (m[0] == "\r" || m[0] == "\n") {
3391 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3392 txt$1.setAttribute("cm-text", m[0]);
3393 builder.col += 1;
3394 } else {
3395 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3396 txt$1.setAttribute("cm-text", m[0]);
3397 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3398 else { content.appendChild(txt$1); }
3399 builder.col += 1;
3400 }
3401 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3402 builder.pos++;
3403 }
3404 }
3405 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3406 if (style || startStyle || endStyle || mustWrap || css) {
3407 var fullStyle = style || "";
3408 if (startStyle) { fullStyle += startStyle; }
3409 if (endStyle) { fullStyle += endStyle; }
3410 var token = elt("span", [content], fullStyle, css);
3411 if (title) { token.title = title; }
3412 return builder.content.appendChild(token)
3413 }
3414 builder.content.appendChild(content);
3415 }
3416
3417 function splitSpaces(text, trailingBefore) {
3418 if (text.length > 1 && !/ /.test(text)) { return text }
3419 var spaceBefore = trailingBefore, result = "";
3420 for (var i = 0; i < text.length; i++) {
3421 var ch = text.charAt(i);
3422 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3423 { ch = "\u00a0"; }
3424 result += ch;
3425 spaceBefore = ch == " ";
3426 }
3427 return result
3428 }
3429
3430 // Work around nonsense dimensions being reported for stretches of
3431 // right-to-left text.
3432 function buildTokenBadBidi(inner, order) {
3433 return function (builder, text, style, startStyle, endStyle, title, css) {
3434 style = style ? style + " cm-force-border" : "cm-force-border";
3435 var start = builder.pos, end = start + text.length;
3436 for (;;) {
3437 // Find the part that overlaps with the start of this text
3438 var part = (void 0);
3439 for (var i = 0; i < order.length; i++) {
3440 part = order[i];
3441 if (part.to > start && part.from <= start) { break }
3442 }
3443 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
3444 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
3445 startStyle = null;
3446 text = text.slice(part.to - start);
3447 start = part.to;
3448 }
3449 }
3450 }
3451
3452 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3453 var widget = !ignoreWidget && marker.widgetNode;
3454 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3455 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3456 if (!widget)
3457 { widget = builder.content.appendChild(document.createElement("span")); }
3458 widget.setAttribute("cm-marker", marker.id);
3459 }
3460 if (widget) {
3461 builder.cm.display.input.setUneditable(widget);
3462 builder.content.appendChild(widget);
3463 }
3464 builder.pos += size;
3465 builder.trailingSpace = false;
3466 }
3467
3468 // Outputs a number of spans to make up a line, taking highlighting
3469 // and marked text into account.
3470 function insertLineContent(line, builder, styles) {
3471 var spans = line.markedSpans, allText = line.text, at = 0;
3472 if (!spans) {
3473 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3474 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3475 return
3476 }
3477
3478 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3479 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
3480 for (;;) {
3481 if (nextChange == pos) { // Update current marker set
3482 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
3483 collapsed = null; nextChange = Infinity;
3484 var foundBookmarks = [], endStyles = (void 0);
3485 for (var j = 0; j < spans.length; ++j) {
3486 var sp = spans[j], m = sp.marker;
3487 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3488 foundBookmarks.push(m);
3489 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3490 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3491 nextChange = sp.to;
3492 spanEndStyle = "";
3493 }
3494 if (m.className) { spanStyle += " " + m.className; }
3495 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3496 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3497 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3498 if (m.title && !title) { title = m.title; }
3499 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3500 { collapsed = sp; }
3501 } else if (sp.from > pos && nextChange > sp.from) {
3502 nextChange = sp.from;
3503 }
3504 }
3505 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3506 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3507
3508 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3509 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3510 if (collapsed && (collapsed.from || 0) == pos) {
3511 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3512 collapsed.marker, collapsed.from == null);
3513 if (collapsed.to == null) { return }
3514 if (collapsed.to == pos) { collapsed = false; }
3515 }
3516 }
3517 if (pos >= len) { break }
3518
3519 var upto = Math.min(len, nextChange);
3520 while (true) {
3521 if (text) {
3522 var end = pos + text.length;
3523 if (!collapsed) {
3524 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3525 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3526 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
3527 }
3528 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3529 pos = end;
3530 spanStartStyle = "";
3531 }
3532 text = allText.slice(at, at = styles[i++]);
3533 style = interpretTokenStyle(styles[i++], builder.cm.options);
3534 }
3535 }
3536 }
3537
3538
3539 // These objects are used to represent the visible (currently drawn)
3540 // part of the document. A LineView may correspond to multiple
3541 // logical lines, if those are connected by collapsed ranges.
3542 function LineView(doc, line, lineN) {
3543 // The starting line
3544 this.line = line;
3545 // Continuing lines, if any
3546 this.rest = visualLineContinued(line);
3547 // Number of logical lines in this visual line
3548 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3549 this.node = this.text = null;
3550 this.hidden = lineIsHidden(doc, line);
3551 }
3552
3553 // Create a range of LineView objects for the given lines.
3554 function buildViewArray(cm, from, to) {
3555 var array = [], nextPos;
3556 for (var pos = from; pos < to; pos = nextPos) {
3557 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3558 nextPos = pos + view.size;
3559 array.push(view);
3560 }
3561 return array
3562 }
3563
3564 var operationGroup = null;
3565
3566 function pushOperation(op) {
3567 if (operationGroup) {
3568 operationGroup.ops.push(op);
3569 } else {
3570 op.ownsGroup = operationGroup = {
3571 ops: [op],
3572 delayedCallbacks: []
3573 };
3574 }
3575 }
3576
3577 function fireCallbacksForOps(group) {
3578 // Calls delayed callbacks and cursorActivity handlers until no
3579 // new ones appear
3580 var callbacks = group.delayedCallbacks, i = 0;
3581 do {
3582 for (; i < callbacks.length; i++)
3583 { callbacks[i].call(null); }
3584 for (var j = 0; j < group.ops.length; j++) {
3585 var op = group.ops[j];
3586 if (op.cursorActivityHandlers)
3587 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3588 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3589 }
3590 } while (i < callbacks.length)
3591 }
3592
3593 function finishOperation(op, endCb) {
3594 var group = op.ownsGroup;
3595 if (!group) { return }
3596
3597 try { fireCallbacksForOps(group); }
3598 finally {
3599 operationGroup = null;
3600 endCb(group);
3601 }
3602 }
3603
3604 var orphanDelayedCallbacks = null;
3605
3606 // Often, we want to signal events at a point where we are in the
3607 // middle of some work, but don't want the handler to start calling
3608 // other methods on the editor, which might be in an inconsistent
3609 // state or simply not expect any other events to happen.
3610 // signalLater looks whether there are any handlers, and schedules
3611 // them to be executed when the last operation ends, or, if no
3612 // operation is active, when a timeout fires.
3613 function signalLater(emitter, type /*, values...*/) {
3614 var arr = getHandlers(emitter, type);
3615 if (!arr.length) { return }
3616 var args = Array.prototype.slice.call(arguments, 2), list;
3617 if (operationGroup) {
3618 list = operationGroup.delayedCallbacks;
3619 } else if (orphanDelayedCallbacks) {
3620 list = orphanDelayedCallbacks;
3621 } else {
3622 list = orphanDelayedCallbacks = [];
3623 setTimeout(fireOrphanDelayed, 0);
3624 }
3625 var loop = function ( i ) {
3626 list.push(function () { return arr[i].apply(null, args); });
3627 };
3628
3629 for (var i = 0; i < arr.length; ++i)
3630 loop( i );
3631 }
3632
3633 function fireOrphanDelayed() {
3634 var delayed = orphanDelayedCallbacks;
3635 orphanDelayedCallbacks = null;
3636 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3637 }
3638
3639 // When an aspect of a line changes, a string is added to
3640 // lineView.changes. This updates the relevant part of the line's
3641 // DOM structure.
3642 function updateLineForChanges(cm, lineView, lineN, dims) {
3643 for (var j = 0; j < lineView.changes.length; j++) {
3644 var type = lineView.changes[j];
3645 if (type == "text") { updateLineText(cm, lineView); }
3646 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3647 else if (type == "class") { updateLineClasses(cm, lineView); }
3648 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3649 }
3650 lineView.changes = null;
3651 }
3652
3653 // Lines with gutter elements, widgets or a background class need to
3654 // be wrapped, and have the extra elements added to the wrapper div
3655 function ensureLineWrapped(lineView) {
3656 if (lineView.node == lineView.text) {
3657 lineView.node = elt("div", null, null, "position: relative");
3658 if (lineView.text.parentNode)
3659 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3660 lineView.node.appendChild(lineView.text);
3661 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3662 }
3663 return lineView.node
3664 }
3665
3666 function updateLineBackground(cm, lineView) {
3667 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3668 if (cls) { cls += " CodeMirror-linebackground"; }
3669 if (lineView.background) {
3670 if (cls) { lineView.background.className = cls; }
3671 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3672 } else if (cls) {
3673 var wrap = ensureLineWrapped(lineView);
3674 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3675 cm.display.input.setUneditable(lineView.background);
3676 }
3677 }
3678
3679 // Wrapper around buildLineContent which will reuse the structure
3680 // in display.externalMeasured when possible.
3681 function getLineContent(cm, lineView) {
3682 var ext = cm.display.externalMeasured;
3683 if (ext && ext.line == lineView.line) {
3684 cm.display.externalMeasured = null;
3685 lineView.measure = ext.measure;
3686 return ext.built
3687 }
3688 return buildLineContent(cm, lineView)
3689 }
3690
3691 // Redraw the line's text. Interacts with the background and text
3692 // classes because the mode may output tokens that influence these
3693 // classes.
3694 function updateLineText(cm, lineView) {
3695 var cls = lineView.text.className;
3696 var built = getLineContent(cm, lineView);
3697 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3698 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3699 lineView.text = built.pre;
3700 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3701 lineView.bgClass = built.bgClass;
3702 lineView.textClass = built.textClass;
3703 updateLineClasses(cm, lineView);
3704 } else if (cls) {
3705 lineView.text.className = cls;
3706 }
3707 }
3708
3709 function updateLineClasses(cm, lineView) {
3710 updateLineBackground(cm, lineView);
3711 if (lineView.line.wrapClass)
3712 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3713 else if (lineView.node != lineView.text)
3714 { lineView.node.className = ""; }
3715 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3716 lineView.text.className = textClass || "";
3717 }
3718
3719 function updateLineGutter(cm, lineView, lineN, dims) {
3720 if (lineView.gutter) {
3721 lineView.node.removeChild(lineView.gutter);
3722 lineView.gutter = null;
3723 }
3724 if (lineView.gutterBackground) {
3725 lineView.node.removeChild(lineView.gutterBackground);
3726 lineView.gutterBackground = null;
3727 }
3728 if (lineView.line.gutterClass) {
3729 var wrap = ensureLineWrapped(lineView);
3730 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3731 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3732 cm.display.input.setUneditable(lineView.gutterBackground);
3733 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3734 }
3735 var markers = lineView.line.gutterMarkers;
3736 if (cm.options.lineNumbers || markers) {
3737 var wrap$1 = ensureLineWrapped(lineView);
3738 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3739 cm.display.input.setUneditable(gutterWrap);
3740 wrap$1.insertBefore(gutterWrap, lineView.text);
3741 if (lineView.line.gutterClass)
3742 { gutterWrap.className += " " + lineView.line.gutterClass; }
3743 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3744 { lineView.lineNumber = gutterWrap.appendChild(
3745 elt("div", lineNumberFor(cm.options, lineN),
3746 "CodeMirror-linenumber CodeMirror-gutter-elt",
3747 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3748 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
3749 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3750 if (found)
3751 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3752 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3753 } }
3754 }
3755 }
3756
3757 function updateLineWidgets(cm, lineView, dims) {
3758 if (lineView.alignable) { lineView.alignable = null; }
3759 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3760 next = node.nextSibling;
3761 if (node.className == "CodeMirror-linewidget")
3762 { lineView.node.removeChild(node); }
3763 }
3764 insertLineWidgets(cm, lineView, dims);
3765 }
3766
3767 // Build a line's DOM representation from scratch
3768 function buildLineElement(cm, lineView, lineN, dims) {
3769 var built = getLineContent(cm, lineView);
3770 lineView.text = lineView.node = built.pre;
3771 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3772 if (built.textClass) { lineView.textClass = built.textClass; }
3773
3774 updateLineClasses(cm, lineView);
3775 updateLineGutter(cm, lineView, lineN, dims);
3776 insertLineWidgets(cm, lineView, dims);
3777 return lineView.node
3778 }
3779
3780 // A lineView may contain multiple logical lines (when merged by
3781 // collapsed spans). The widgets for all of them need to be drawn.
3782 function insertLineWidgets(cm, lineView, dims) {
3783 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3784 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3785 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3786 }
3787
3788 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3789 if (!line.widgets) { return }
3790 var wrap = ensureLineWrapped(lineView);
3791 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3792 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3793 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3794 positionLineWidget(widget, node, lineView, dims);
3795 cm.display.input.setUneditable(node);
3796 if (allowAbove && widget.above)
3797 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3798 else
3799 { wrap.appendChild(node); }
3800 signalLater(widget, "redraw");
3801 }
3802 }
3803
3804 function positionLineWidget(widget, node, lineView, dims) {
3805 if (widget.noHScroll) {
3806 (lineView.alignable || (lineView.alignable = [])).push(node);
3807 var width = dims.wrapperWidth;
3808 node.style.left = dims.fixedPos + "px";
3809 if (!widget.coverGutter) {
3810 width -= dims.gutterTotalWidth;
3811 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3812 }
3813 node.style.width = width + "px";
3814 }
3815 if (widget.coverGutter) {
3816 node.style.zIndex = 5;
3817 node.style.position = "relative";
3818 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3819 }
3820 }
3821
3822 function widgetHeight(widget) {
3823 if (widget.height != null) { return widget.height }
3824 var cm = widget.doc.cm;
3825 if (!cm) { return 0 }
3826 if (!contains(document.body, widget.node)) {
3827 var parentStyle = "position: relative;";
3828 if (widget.coverGutter)
3829 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3830 if (widget.noHScroll)
3831 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3832 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3833 }
3834 return widget.height = widget.node.parentNode.offsetHeight
3835 }
3836
3837 // Return true when the given mouse event happened in a widget
3838 function eventInWidget(display, e) {
3839 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3840 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3841 (n.parentNode == display.sizer && n != display.mover))
3842 { return true }
3843 }
3844 }
3845
3846 // POSITION MEASUREMENT
3847
3848 function paddingTop(display) {return display.lineSpace.offsetTop}
3849 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3850 function paddingH(display) {
3851 if (display.cachedPaddingH) { return display.cachedPaddingH }
3852 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3853 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3854 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3855 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3856 return data
3857 }
3858
3859 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3860 function displayWidth(cm) {
3861 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3862 }
3863 function displayHeight(cm) {
3864 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3865 }
3866
3867 // Ensure the lineView.wrapping.heights array is populated. This is
3868 // an array of bottom offsets for the lines that make up a drawn
3869 // line. When lineWrapping is on, there might be more than one
3870 // height.
3871 function ensureLineHeights(cm, lineView, rect) {
3872 var wrapping = cm.options.lineWrapping;
3873 var curWidth = wrapping && displayWidth(cm);
3874 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3875 var heights = lineView.measure.heights = [];
3876 if (wrapping) {
3877 lineView.measure.width = curWidth;
3878 var rects = lineView.text.firstChild.getClientRects();
3879 for (var i = 0; i < rects.length - 1; i++) {
3880 var cur = rects[i], next = rects[i + 1];
3881 if (Math.abs(cur.bottom - next.bottom) > 2)
3882 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
3883 }
3884 }
3885 heights.push(rect.bottom - rect.top);
3886 }
3887 }
3888
3889 // Find a line map (mapping character offsets to text nodes) and a
3890 // measurement cache for the given line number. (A line view might
3891 // contain multiple lines when collapsed ranges are present.)
3892 function mapFromLineView(lineView, line, lineN) {
3893 if (lineView.line == line)
3894 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
3895 for (var i = 0; i < lineView.rest.length; i++)
3896 { if (lineView.rest[i] == line)
3897 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
3898 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
3899 { if (lineNo(lineView.rest[i$1]) > lineN)
3900 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
3901 }
3902
3903 // Render a line into the hidden node display.externalMeasured. Used
3904 // when measurement is needed for a line that's not in the viewport.
3905 function updateExternalMeasurement(cm, line) {
3906 line = visualLine(line);
3907 var lineN = lineNo(line);
3908 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
3909 view.lineN = lineN;
3910 var built = view.built = buildLineContent(cm, view);
3911 view.text = built.pre;
3912 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
3913 return view
3914 }
3915
3916 // Get a {top, bottom, left, right} box (in line-local coordinates)
3917 // for a given character.
3918 function measureChar(cm, line, ch, bias) {
3919 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
3920 }
3921
3922 // Find a line view that corresponds to the given line number.
3923 function findViewForLine(cm, lineN) {
3924 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
3925 { return cm.display.view[findViewIndex(cm, lineN)] }
3926 var ext = cm.display.externalMeasured;
3927 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
3928 { return ext }
3929 }
3930
3931 // Measurement can be split in two steps, the set-up work that
3932 // applies to the whole line, and the measurement of the actual
3933 // character. Functions like coordsChar, that need to do a lot of
3934 // measurements in a row, can thus ensure that the set-up work is
3935 // only done once.
3936 function prepareMeasureForLine(cm, line) {
3937 var lineN = lineNo(line);
3938 var view = findViewForLine(cm, lineN);
3939 if (view && !view.text) {
3940 view = null;
3941 } else if (view && view.changes) {
3942 updateLineForChanges(cm, view, lineN, getDimensions(cm));
3943 cm.curOp.forceUpdate = true;
3944 }
3945 if (!view)
3946 { view = updateExternalMeasurement(cm, line); }
3947
3948 var info = mapFromLineView(view, line, lineN);
3949 return {
3950 line: line, view: view, rect: null,
3951 map: info.map, cache: info.cache, before: info.before,
3952 hasHeights: false
3953 }
3954 }
3955
3956 // Given a prepared measurement object, measures the position of an
3957 // actual character (or fetches it from the cache).
3958 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
3959 if (prepared.before) { ch = -1; }
3960 var key = ch + (bias || ""), found;
3961 if (prepared.cache.hasOwnProperty(key)) {
3962 found = prepared.cache[key];
3963 } else {
3964 if (!prepared.rect)
3965 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
3966 if (!prepared.hasHeights) {
3967 ensureLineHeights(cm, prepared.view, prepared.rect);
3968 prepared.hasHeights = true;
3969 }
3970 found = measureCharInner(cm, prepared, ch, bias);
3971 if (!found.bogus) { prepared.cache[key] = found; }
3972 }
3973 return {left: found.left, right: found.right,
3974 top: varHeight ? found.rtop : found.top,
3975 bottom: varHeight ? found.rbottom : found.bottom}
3976 }
3977
3978 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
3979
3980 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
3981 var node, start, end, collapse, mStart, mEnd;
3982 // First, search the line map for the text node corresponding to,
3983 // or closest to, the target character.
3984 for (var i = 0; i < map$$1.length; i += 3) {
3985 mStart = map$$1[i];
3986 mEnd = map$$1[i + 1];
3987 if (ch < mStart) {
3988 start = 0; end = 1;
3989 collapse = "left";
3990 } else if (ch < mEnd) {
3991 start = ch - mStart;
3992 end = start + 1;
3993 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
3994 end = mEnd - mStart;
3995 start = end - 1;
3996 if (ch >= mEnd) { collapse = "right"; }
3997 }
3998 if (start != null) {
3999 node = map$$1[i + 2];
4000 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4001 { collapse = bias; }
4002 if (bias == "left" && start == 0)
4003 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
4004 node = map$$1[(i -= 3) + 2];
4005 collapse = "left";
4006 } }
4007 if (bias == "right" && start == mEnd - mStart)
4008 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
4009 node = map$$1[(i += 3) + 2];
4010 collapse = "right";
4011 } }
4012 break
4013 }
4014 }
4015 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4016 }
4017
4018 function getUsefulRect(rects, bias) {
4019 var rect = nullRect;
4020 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4021 if ((rect = rects[i]).left != rect.right) { break }
4022 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4023 if ((rect = rects[i$1]).left != rect.right) { break }
4024 } }
4025 return rect
4026 }
4027
4028 function measureCharInner(cm, prepared, ch, bias) {
4029 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4030 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4031
4032 var rect;
4033 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4034 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4035 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4036 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4037 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4038 { rect = node.parentNode.getBoundingClientRect(); }
4039 else
4040 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4041 if (rect.left || rect.right || start == 0) { break }
4042 end = start;
4043 start = start - 1;
4044 collapse = "right";
4045 }
4046 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4047 } else { // If it is a widget, simply get the box for the whole widget.
4048 if (start > 0) { collapse = bias = "right"; }
4049 var rects;
4050 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4051 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4052 else
4053 { rect = node.getBoundingClientRect(); }
4054 }
4055 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4056 var rSpan = node.parentNode.getClientRects()[0];
4057 if (rSpan)
4058 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4059 else
4060 { rect = nullRect; }
4061 }
4062
4063 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4064 var mid = (rtop + rbot) / 2;
4065 var heights = prepared.view.measure.heights;
4066 var i = 0;
4067 for (; i < heights.length - 1; i++)
4068 { if (mid < heights[i]) { break } }
4069 var top = i ? heights[i - 1] : 0, bot = heights[i];
4070 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4071 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4072 top: top, bottom: bot};
4073 if (!rect.left && !rect.right) { result.bogus = true; }
4074 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4075
4076 return result
4077 }
4078
4079 // Work around problem with bounding client rects on ranges being
4080 // returned incorrectly when zoomed on IE10 and below.
4081 function maybeUpdateRectForZooming(measure, rect) {
4082 if (!window.screen || screen.logicalXDPI == null ||
4083 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4084 { return rect }
4085 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4086 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4087 return {left: rect.left * scaleX, right: rect.right * scaleX,
4088 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4089 }
4090
4091 function clearLineMeasurementCacheFor(lineView) {
4092 if (lineView.measure) {
4093 lineView.measure.cache = {};
4094 lineView.measure.heights = null;
4095 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4096 { lineView.measure.caches[i] = {}; } }
4097 }
4098 }
4099
4100 function clearLineMeasurementCache(cm) {
4101 cm.display.externalMeasure = null;
4102 removeChildren(cm.display.lineMeasure);
4103 for (var i = 0; i < cm.display.view.length; i++)
4104 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4105 }
4106
4107 function clearCaches(cm) {
4108 clearLineMeasurementCache(cm);
4109 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4110 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4111 cm.display.lineNumChars = null;
4112 }
4113
4114 function pageScrollX() {
4115 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4116 // which causes page_Offset and bounding client rects to use
4117 // different reference viewports and invalidate our calculations.
4118 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4119 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4120 }
4121 function pageScrollY() {
4122 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4123 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4124 }
4125
4126 // Converts a {top, bottom, left, right} box from line-local
4127 // coordinates into another coordinate system. Context may be one of
4128 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4129 // or "page".
4130 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4131 if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
4132 var size = widgetHeight(lineObj.widgets[i]);
4133 rect.top += size; rect.bottom += size;
4134 } } }
4135 if (context == "line") { return rect }
4136 if (!context) { context = "local"; }
4137 var yOff = heightAtLine(lineObj);
4138 if (context == "local") { yOff += paddingTop(cm.display); }
4139 else { yOff -= cm.display.viewOffset; }
4140 if (context == "page" || context == "window") {
4141 var lOff = cm.display.lineSpace.getBoundingClientRect();
4142 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4143 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4144 rect.left += xOff; rect.right += xOff;
4145 }
4146 rect.top += yOff; rect.bottom += yOff;
4147 return rect
4148 }
4149
4150 // Coverts a box from "div" coords to another coordinate system.
4151 // Context may be "window", "page", "div", or "local"./null.
4152 function fromCoordSystem(cm, coords, context) {
4153 if (context == "div") { return coords }
4154 var left = coords.left, top = coords.top;
4155 // First move into "page" coordinate system
4156 if (context == "page") {
4157 left -= pageScrollX();
4158 top -= pageScrollY();
4159 } else if (context == "local" || !context) {
4160 var localBox = cm.display.sizer.getBoundingClientRect();
4161 left += localBox.left;
4162 top += localBox.top;
4163 }
4164
4165 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4166 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4167 }
4168
4169 function charCoords(cm, pos, context, lineObj, bias) {
4170 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4171 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4172 }
4173
4174 // Returns a box for a given cursor position, which may have an
4175 // 'other' property containing the position of the secondary cursor
4176 // on a bidi boundary.
4177 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4178 // and after `char - 1` in writing order of `char - 1`
4179 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4180 // and before `char` in writing order of `char`
4181 // Examples (upper-case letters are RTL, lower-case are LTR):
4182 // Pos(0, 1, ...)
4183 // before after
4184 // ab a|b a|b
4185 // aB a|B aB|
4186 // Ab |Ab A|b
4187 // AB B|A B|A
4188 // Every position after the last character on a line is considered to stick
4189 // to the last character on the line.
4190 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4191 lineObj = lineObj || getLine(cm.doc, pos.line);
4192 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4193 function get(ch, right) {
4194 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4195 if (right) { m.left = m.right; } else { m.right = m.left; }
4196 return intoCoordSystem(cm, lineObj, m, context)
4197 }
4198 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4199 if (ch >= lineObj.text.length) {
4200 ch = lineObj.text.length;
4201 sticky = "before";
4202 } else if (ch <= 0) {
4203 ch = 0;
4204 sticky = "after";
4205 }
4206 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4207
4208 function getBidi(ch, partPos, invert) {
4209 var part = order[partPos], right = (part.level % 2) != 0;
4210 return get(invert ? ch - 1 : ch, right != invert)
4211 }
4212 var partPos = getBidiPartAt(order, ch, sticky);
4213 var other = bidiOther;
4214 var val = getBidi(ch, partPos, sticky == "before");
4215 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4216 return val
4217 }
4218
4219 // Used to cheaply estimate the coordinates for a position. Used for
4220 // intermediate scroll updates.
4221 function estimateCoords(cm, pos) {
4222 var left = 0;
4223 pos = clipPos(cm.doc, pos);
4224 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4225 var lineObj = getLine(cm.doc, pos.line);
4226 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4227 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4228 }
4229
4230 // Positions returned by coordsChar contain some extra information.
4231 // xRel is the relative x position of the input coordinates compared
4232 // to the found position (so xRel > 0 means the coordinates are to
4233 // the right of the character position, for example). When outside
4234 // is true, that means the coordinates lie outside the line's
4235 // vertical range.
4236 function PosWithInfo(line, ch, sticky, outside, xRel) {
4237 var pos = Pos(line, ch, sticky);
4238 pos.xRel = xRel;
4239 if (outside) { pos.outside = true; }
4240 return pos
4241 }
4242
4243 // Compute the character position closest to the given coordinates.
4244 // Input must be lineSpace-local ("div" coordinate system).
4245 function coordsChar(cm, x, y) {
4246 var doc = cm.doc;
4247 y += cm.display.viewOffset;
4248 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4249 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4250 if (lineN > last)
4251 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4252 if (x < 0) { x = 0; }
4253
4254 var lineObj = getLine(doc, lineN);
4255 for (;;) {
4256 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4257 var merged = collapsedSpanAtEnd(lineObj);
4258 var mergedPos = merged && merged.find(0, true);
4259 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
4260 { lineN = lineNo(lineObj = mergedPos.to.line); }
4261 else
4262 { return found }
4263 }
4264 }
4265
4266 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4267 var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); };
4268 var end = lineObj.text.length;
4269 var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0);
4270 end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end);
4271 return {begin: begin, end: end}
4272 }
4273
4274 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4275 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4276 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4277 }
4278
4279 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4280 y -= heightAtLine(lineObj);
4281 var begin = 0, end = lineObj.text.length;
4282 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4283 var pos;
4284 var order = getOrder(lineObj, cm.doc.direction);
4285 if (order) {
4286 if (cm.options.lineWrapping) {
4287 var assign;
4288 ((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign));
4289 }
4290 pos = new Pos(lineNo$$1, Math.floor(begin + (end - begin) / 2));
4291 var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left;
4292 var dir = beginLeft < x ? 1 : -1;
4293 var prevDiff, diff = beginLeft - x, prevPos;
4294 var steps = Math.ceil((end - begin) / 4);
4295 outer: do {
4296 prevDiff = diff;
4297 prevPos = pos;
4298 var i = 0;
4299 for (; i < steps; ++i) {
4300 var prevPos$1 = pos;
4301 pos = moveVisually(cm, lineObj, pos, dir);
4302 if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) {
4303 pos = prevPos$1;
4304 break outer
4305 }
4306 }
4307 diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x;
4308 if (steps > 1) {
4309 var diff_change_per_step = Math.abs(diff - prevDiff) / steps;
4310 steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step));
4311 dir = diff < 0 ? 1 : -1;
4312 }
4313 } while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))))
4314 if (Math.abs(diff) > Math.abs(prevDiff)) {
4315 if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
4316 pos = prevPos;
4317 }
4318 } else {
4319 var ch = findFirst(function (ch) {
4320 var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line");
4321 if (box.top > y) {
4322 // For the cursor stickiness
4323 end = Math.min(ch, end);
4324 return true
4325 }
4326 else if (box.bottom <= y) { return false }
4327 else if (box.left > x) { return true }
4328 else if (box.right < x) { return false }
4329 else { return (x - box.left < box.right - x) }
4330 }, begin, end);
4331 ch = skipExtendingChars(lineObj.text, ch, 1);
4332 pos = new Pos(lineNo$$1, ch, ch == end ? "before" : "after");
4333 }
4334 var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure);
4335 if (y < coords.top || coords.bottom < y) { pos.outside = true; }
4336 pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0);
4337 return pos
4338 }
4339
4340 var measureText;
4341 // Compute the default text height.
4342 function textHeight(display) {
4343 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4344 if (measureText == null) {
4345 measureText = elt("pre");
4346 // Measure a bunch of lines, for browsers that compute
4347 // fractional heights.
4348 for (var i = 0; i < 49; ++i) {
4349 measureText.appendChild(document.createTextNode("x"));
4350 measureText.appendChild(elt("br"));
4351 }
4352 measureText.appendChild(document.createTextNode("x"));
4353 }
4354 removeChildrenAndAdd(display.measure, measureText);
4355 var height = measureText.offsetHeight / 50;
4356 if (height > 3) { display.cachedTextHeight = height; }
4357 removeChildren(display.measure);
4358 return height || 1
4359 }
4360
4361 // Compute the default character width.
4362 function charWidth(display) {
4363 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4364 var anchor = elt("span", "xxxxxxxxxx");
4365 var pre = elt("pre", [anchor]);
4366 removeChildrenAndAdd(display.measure, pre);
4367 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4368 if (width > 2) { display.cachedCharWidth = width; }
4369 return width || 10
4370 }
4371
4372 // Do a bulk-read of the DOM positions and sizes needed to draw the
4373 // view, so that we don't interleave reading and writing to the DOM.
4374 function getDimensions(cm) {
4375 var d = cm.display, left = {}, width = {};
4376 var gutterLeft = d.gutters.clientLeft;
4377 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4378 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
4379 width[cm.options.gutters[i]] = n.clientWidth;
4380 }
4381 return {fixedPos: compensateForHScroll(d),
4382 gutterTotalWidth: d.gutters.offsetWidth,
4383 gutterLeft: left,
4384 gutterWidth: width,
4385 wrapperWidth: d.wrapper.clientWidth}
4386 }
4387
4388 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4389 // but using getBoundingClientRect to get a sub-pixel-accurate
4390 // result.
4391 function compensateForHScroll(display) {
4392 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4393 }
4394
4395 // Returns a function that estimates the height of a line, to use as
4396 // first approximation until the line becomes visible (and is thus
4397 // properly measurable).
4398 function estimateHeight(cm) {
4399 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4400 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4401 return function (line) {
4402 if (lineIsHidden(cm.doc, line)) { return 0 }
4403
4404 var widgetsHeight = 0;
4405 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4406 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4407 } }
4408
4409 if (wrapping)
4410 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4411 else
4412 { return widgetsHeight + th }
4413 }
4414 }
4415
4416 function estimateLineHeights(cm) {
4417 var doc = cm.doc, est = estimateHeight(cm);
4418 doc.iter(function (line) {
4419 var estHeight = est(line);
4420 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4421 });
4422 }
4423
4424 // Given a mouse event, find the corresponding position. If liberal
4425 // is false, it checks whether a gutter or scrollbar was clicked,
4426 // and returns null if it was. forRect is used by rectangular
4427 // selections, and tries to estimate a character position even for
4428 // coordinates beyond the right of the text.
4429 function posFromMouse(cm, e, liberal, forRect) {
4430 var display = cm.display;
4431 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4432
4433 var x, y, space = display.lineSpace.getBoundingClientRect();
4434 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4435 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4436 catch (e) { return null }
4437 var coords = coordsChar(cm, x, y), line;
4438 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4439 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4440 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4441 }
4442 return coords
4443 }
4444
4445 // Find the view element corresponding to a given line. Return null
4446 // when the line isn't visible.
4447 function findViewIndex(cm, n) {
4448 if (n >= cm.display.viewTo) { return null }
4449 n -= cm.display.viewFrom;
4450 if (n < 0) { return null }
4451 var view = cm.display.view;
4452 for (var i = 0; i < view.length; i++) {
4453 n -= view[i].size;
4454 if (n < 0) { return i }
4455 }
4456 }
4457
4458 function updateSelection(cm) {
4459 cm.display.input.showSelection(cm.display.input.prepareSelection());
4460 }
4461
4462 function prepareSelection(cm, primary) {
4463 var doc = cm.doc, result = {};
4464 var curFragment = result.cursors = document.createDocumentFragment();
4465 var selFragment = result.selection = document.createDocumentFragment();
4466
4467 for (var i = 0; i < doc.sel.ranges.length; i++) {
4468 if (primary === false && i == doc.sel.primIndex) { continue }
4469 var range$$1 = doc.sel.ranges[i];
4470 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4471 var collapsed = range$$1.empty();
4472 if (collapsed || cm.options.showCursorWhenSelecting)
4473 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4474 if (!collapsed)
4475 { drawSelectionRange(cm, range$$1, selFragment); }
4476 }
4477 return result
4478 }
4479
4480 // Draws a cursor for the given range
4481 function drawSelectionCursor(cm, head, output) {
4482 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4483
4484 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4485 cursor.style.left = pos.left + "px";
4486 cursor.style.top = pos.top + "px";
4487 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4488
4489 if (pos.other) {
4490 // Secondary cursor, shown when on a 'jump' in bi-directional text
4491 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4492 otherCursor.style.display = "";
4493 otherCursor.style.left = pos.other.left + "px";
4494 otherCursor.style.top = pos.other.top + "px";
4495 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4496 }
4497 }
4498
4499 // Draws the given range as a highlighted selection
4500 function drawSelectionRange(cm, range$$1, output) {
4501 var display = cm.display, doc = cm.doc;
4502 var fragment = document.createDocumentFragment();
4503 var padding = paddingH(cm.display), leftSide = padding.left;
4504 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4505
4506 function add(left, top, width, bottom) {
4507 if (top < 0) { top = 0; }
4508 top = Math.round(top);
4509 bottom = Math.round(bottom);
4510 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")));
4511 }
4512
4513 function drawForLine(line, fromArg, toArg) {
4514 var lineObj = getLine(doc, line);
4515 var lineLen = lineObj.text.length;
4516 var start, end;
4517 function coords(ch, bias) {
4518 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4519 }
4520
4521 iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
4522 var leftPos = coords(from, "left"), rightPos, left, right;
4523 if (from == to) {
4524 rightPos = leftPos;
4525 left = right = leftPos.left;
4526 } else {
4527 rightPos = coords(to - 1, "right");
4528 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
4529 left = leftPos.left;
4530 right = rightPos.right;
4531 }
4532 if (fromArg == null && from == 0) { left = leftSide; }
4533 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
4534 add(left, leftPos.top, null, leftPos.bottom);
4535 left = leftSide;
4536 if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top); }
4537 }
4538 if (toArg == null && to == lineLen) { right = rightSide; }
4539 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
4540 { start = leftPos; }
4541 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
4542 { end = rightPos; }
4543 if (left < leftSide + 1) { left = leftSide; }
4544 add(left, rightPos.top, right - left, rightPos.bottom);
4545 });
4546 return {start: start, end: end}
4547 }
4548
4549 var sFrom = range$$1.from(), sTo = range$$1.to();
4550 if (sFrom.line == sTo.line) {
4551 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4552 } else {
4553 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4554 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4555 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4556 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4557 if (singleVLine) {
4558 if (leftEnd.top < rightStart.top - 2) {
4559 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4560 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4561 } else {
4562 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4563 }
4564 }
4565 if (leftEnd.bottom < rightStart.top)
4566 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4567 }
4568
4569 output.appendChild(fragment);
4570 }
4571
4572 // Cursor-blinking
4573 function restartBlink(cm) {
4574 if (!cm.state.focused) { return }
4575 var display = cm.display;
4576 clearInterval(display.blinker);
4577 var on = true;
4578 display.cursorDiv.style.visibility = "";
4579 if (cm.options.cursorBlinkRate > 0)
4580 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4581 cm.options.cursorBlinkRate); }
4582 else if (cm.options.cursorBlinkRate < 0)
4583 { display.cursorDiv.style.visibility = "hidden"; }
4584 }
4585
4586 function ensureFocus(cm) {
4587 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4588 }
4589
4590 function delayBlurEvent(cm) {
4591 cm.state.delayingBlurEvent = true;
4592 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4593 cm.state.delayingBlurEvent = false;
4594 onBlur(cm);
4595 } }, 100);
4596 }
4597
4598 function onFocus(cm, e) {
4599 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4600
4601 if (cm.options.readOnly == "nocursor") { return }
4602 if (!cm.state.focused) {
4603 signal(cm, "focus", cm, e);
4604 cm.state.focused = true;
4605 addClass(cm.display.wrapper, "CodeMirror-focused");
4606 // This test prevents this from firing when a context
4607 // menu is closed (since the input reset would kill the
4608 // select-all detection hack)
4609 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4610 cm.display.input.reset();
4611 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4612 }
4613 cm.display.input.receivedFocus();
4614 }
4615 restartBlink(cm);
4616 }
4617 function onBlur(cm, e) {
4618 if (cm.state.delayingBlurEvent) { return }
4619
4620 if (cm.state.focused) {
4621 signal(cm, "blur", cm, e);
4622 cm.state.focused = false;
4623 rmClass(cm.display.wrapper, "CodeMirror-focused");
4624 }
4625 clearInterval(cm.display.blinker);
4626 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4627 }
4628
4629 // Read the actual heights of the rendered lines, and update their
4630 // stored heights to match.
4631 function updateHeightsInViewport(cm) {
4632 var display = cm.display;
4633 var prevBottom = display.lineDiv.offsetTop;
4634 for (var i = 0; i < display.view.length; i++) {
4635 var cur = display.view[i], height = (void 0);
4636 if (cur.hidden) { continue }
4637 if (ie && ie_version < 8) {
4638 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4639 height = bot - prevBottom;
4640 prevBottom = bot;
4641 } else {
4642 var box = cur.node.getBoundingClientRect();
4643 height = box.bottom - box.top;
4644 }
4645 var diff = cur.line.height - height;
4646 if (height < 2) { height = textHeight(display); }
4647 if (diff > .005 || diff < -.005) {
4648 updateLineHeight(cur.line, height);
4649 updateWidgetHeight(cur.line);
4650 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4651 { updateWidgetHeight(cur.rest[j]); } }
4652 }
4653 }
4654 }
4655
4656 // Read and store the height of line widgets associated with the
4657 // given line.
4658 function updateWidgetHeight(line) {
4659 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
4660 { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight; } }
4661 }
4662
4663 // Compute the lines that are visible in a given viewport (defaults
4664 // the the current scroll position). viewport may contain top,
4665 // height, and ensure (see op.scrollToPos) properties.
4666 function visibleLines(display, doc, viewport) {
4667 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4668 top = Math.floor(top - paddingTop(display));
4669 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4670
4671 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4672 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4673 // forces those lines into the viewport (if possible).
4674 if (viewport && viewport.ensure) {
4675 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4676 if (ensureFrom < from) {
4677 from = ensureFrom;
4678 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4679 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4680 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4681 to = ensureTo;
4682 }
4683 }
4684 return {from: from, to: Math.max(to, from + 1)}
4685 }
4686
4687 // Re-align line numbers and gutter marks to compensate for
4688 // horizontal scrolling.
4689 function alignHorizontally(cm) {
4690 var display = cm.display, view = display.view;
4691 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4692 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4693 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4694 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4695 if (cm.options.fixedGutter) {
4696 if (view[i].gutter)
4697 { view[i].gutter.style.left = left; }
4698 if (view[i].gutterBackground)
4699 { view[i].gutterBackground.style.left = left; }
4700 }
4701 var align = view[i].alignable;
4702 if (align) { for (var j = 0; j < align.length; j++)
4703 { align[j].style.left = left; } }
4704 } }
4705 if (cm.options.fixedGutter)
4706 { display.gutters.style.left = (comp + gutterW) + "px"; }
4707 }
4708
4709 // Used to ensure that the line number gutter is still the right
4710 // size for the current document size. Returns true when an update
4711 // is needed.
4712 function maybeUpdateLineNumberWidth(cm) {
4713 if (!cm.options.lineNumbers) { return false }
4714 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4715 if (last.length != display.lineNumChars) {
4716 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4717 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4718 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4719 display.lineGutter.style.width = "";
4720 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4721 display.lineNumWidth = display.lineNumInnerWidth + padding;
4722 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4723 display.lineGutter.style.width = display.lineNumWidth + "px";
4724 updateGutterSpace(cm);
4725 return true
4726 }
4727 return false
4728 }
4729
4730 // SCROLLING THINGS INTO VIEW
4731
4732 // If an editor sits on the top or bottom of the window, partially
4733 // scrolled out of view, this ensures that the cursor is visible.
4734 function maybeScrollWindow(cm, rect) {
4735 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4736
4737 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4738 if (rect.top + box.top < 0) { doScroll = true; }
4739 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4740 if (doScroll != null && !phantom) {
4741 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;"));
4742 cm.display.lineSpace.appendChild(scrollNode);
4743 scrollNode.scrollIntoView(doScroll);
4744 cm.display.lineSpace.removeChild(scrollNode);
4745 }
4746 }
4747
4748 // Scroll a given position into view (immediately), verifying that
4749 // it actually became visible (as line heights are accurately
4750 // measured, the position of something may 'drift' during drawing).
4751 function scrollPosIntoView(cm, pos, end, margin) {
4752 if (margin == null) { margin = 0; }
4753 var rect;
4754 if (!cm.options.lineWrapping && pos == end) {
4755 // Set pos and end to the cursor positions around the character pos sticks to
4756 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4757 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4758 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
4759 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
4760 }
4761 for (var limit = 0; limit < 5; limit++) {
4762 var changed = false;
4763 var coords = cursorCoords(cm, pos);
4764 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4765 rect = {left: Math.min(coords.left, endCoords.left),
4766 top: Math.min(coords.top, endCoords.top) - margin,
4767 right: Math.max(coords.left, endCoords.left),
4768 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
4769 var scrollPos = calculateScrollPos(cm, rect);
4770 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4771 if (scrollPos.scrollTop != null) {
4772 updateScrollTop(cm, scrollPos.scrollTop);
4773 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
4774 }
4775 if (scrollPos.scrollLeft != null) {
4776 setScrollLeft(cm, scrollPos.scrollLeft);
4777 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
4778 }
4779 if (!changed) { break }
4780 }
4781 return rect
4782 }
4783
4784 // Scroll a given set of coordinates into view (immediately).
4785 function scrollIntoView(cm, rect) {
4786 var scrollPos = calculateScrollPos(cm, rect);
4787 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
4788 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
4789 }
4790
4791 // Calculate a new scroll position needed to scroll the given
4792 // rectangle into view. Returns an object with scrollTop and
4793 // scrollLeft properties. When these are undefined, the
4794 // vertical/horizontal position does not need to be adjusted.
4795 function calculateScrollPos(cm, rect) {
4796 var display = cm.display, snapMargin = textHeight(cm.display);
4797 if (rect.top < 0) { rect.top = 0; }
4798 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4799 var screen = displayHeight(cm), result = {};
4800 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
4801 var docBottom = cm.doc.height + paddingVert(display);
4802 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
4803 if (rect.top < screentop) {
4804 result.scrollTop = atTop ? 0 : rect.top;
4805 } else if (rect.bottom > screentop + screen) {
4806 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
4807 if (newTop != screentop) { result.scrollTop = newTop; }
4808 }
4809
4810 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4811 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4812 var tooWide = rect.right - rect.left > screenw;
4813 if (tooWide) { rect.right = rect.left + screenw; }
4814 if (rect.left < 10)
4815 { result.scrollLeft = 0; }
4816 else if (rect.left < screenleft)
4817 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
4818 else if (rect.right > screenw + screenleft - 3)
4819 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
4820 return result
4821 }
4822
4823 // Store a relative adjustment to the scroll position in the current
4824 // operation (to be applied when the operation finishes).
4825 function addToScrollTop(cm, top) {
4826 if (top == null) { return }
4827 resolveScrollToPos(cm);
4828 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4829 }
4830
4831 // Make sure that at the end of the operation the current cursor is
4832 // shown.
4833 function ensureCursorVisible(cm) {
4834 resolveScrollToPos(cm);
4835 var cur = cm.getCursor();
4836 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
4837 }
4838
4839 function scrollToCoords(cm, x, y) {
4840 if (x != null || y != null) { resolveScrollToPos(cm); }
4841 if (x != null) { cm.curOp.scrollLeft = x; }
4842 if (y != null) { cm.curOp.scrollTop = y; }
4843 }
4844
4845 function scrollToRange(cm, range$$1) {
4846 resolveScrollToPos(cm);
4847 cm.curOp.scrollToPos = range$$1;
4848 }
4849
4850 // When an operation has its scrollToPos property set, and another
4851 // scroll action is applied before the end of the operation, this
4852 // 'simulates' scrolling that position into view in a cheap way, so
4853 // that the effect of intermediate scroll commands is not ignored.
4854 function resolveScrollToPos(cm) {
4855 var range$$1 = cm.curOp.scrollToPos;
4856 if (range$$1) {
4857 cm.curOp.scrollToPos = null;
4858 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
4859 scrollToCoordsRange(cm, from, to, range$$1.margin);
4860 }
4861 }
4862
4863 function scrollToCoordsRange(cm, from, to, margin) {
4864 var sPos = calculateScrollPos(cm, {
4865 left: Math.min(from.left, to.left),
4866 top: Math.min(from.top, to.top) - margin,
4867 right: Math.max(from.right, to.right),
4868 bottom: Math.max(from.bottom, to.bottom) + margin
4869 });
4870 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
4871 }
4872
4873 // Sync the scrollable area and scrollbars, ensure the viewport
4874 // covers the visible area.
4875 function updateScrollTop(cm, val) {
4876 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
4877 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
4878 setScrollTop(cm, val, true);
4879 if (gecko) { updateDisplaySimple(cm); }
4880 startWorker(cm, 100);
4881 }
4882
4883 function setScrollTop(cm, val, forceScroll) {
4884 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
4885 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
4886 cm.doc.scrollTop = val;
4887 cm.display.scrollbars.setScrollTop(val);
4888 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
4889 }
4890
4891 // Sync scroller and scrollbar, ensure the gutter elements are
4892 // aligned.
4893 function setScrollLeft(cm, val, isScroller, forceScroll) {
4894 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
4895 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
4896 cm.doc.scrollLeft = val;
4897 alignHorizontally(cm);
4898 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
4899 cm.display.scrollbars.setScrollLeft(val);
4900 }
4901
4902 // SCROLLBARS
4903
4904 // Prepare DOM reads needed to update the scrollbars. Done in one
4905 // shot to minimize update/measure roundtrips.
4906 function measureForScrollbars(cm) {
4907 var d = cm.display, gutterW = d.gutters.offsetWidth;
4908 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
4909 return {
4910 clientHeight: d.scroller.clientHeight,
4911 viewHeight: d.wrapper.clientHeight,
4912 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
4913 viewWidth: d.wrapper.clientWidth,
4914 barLeft: cm.options.fixedGutter ? gutterW : 0,
4915 docHeight: docH,
4916 scrollHeight: docH + scrollGap(cm) + d.barHeight,
4917 nativeBarWidth: d.nativeBarWidth,
4918 gutterWidth: gutterW
4919 }
4920 }
4921
4922 var NativeScrollbars = function(place, scroll, cm) {
4923 this.cm = cm;
4924 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
4925 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
4926 place(vert); place(horiz);
4927
4928 on(vert, "scroll", function () {
4929 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
4930 });
4931 on(horiz, "scroll", function () {
4932 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
4933 });
4934
4935 this.checkedZeroWidth = false;
4936 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
4937 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
4938 };
4939
4940 NativeScrollbars.prototype.update = function (measure) {
4941 var needsH = measure.scrollWidth > measure.clientWidth + 1;
4942 var needsV = measure.scrollHeight > measure.clientHeight + 1;
4943 var sWidth = measure.nativeBarWidth;
4944
4945 if (needsV) {
4946 this.vert.style.display = "block";
4947 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
4948 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
4949 // A bug in IE8 can cause this value to be negative, so guard it.
4950 this.vert.firstChild.style.height =
4951 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
4952 } else {
4953 this.vert.style.display = "";
4954 this.vert.firstChild.style.height = "0";
4955 }
4956
4957 if (needsH) {
4958 this.horiz.style.display = "block";
4959 this.horiz.style.right = needsV ? sWidth + "px" : "0";
4960 this.horiz.style.left = measure.barLeft + "px";
4961 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
4962 this.horiz.firstChild.style.width =
4963 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
4964 } else {
4965 this.horiz.style.display = "";
4966 this.horiz.firstChild.style.width = "0";
4967 }
4968
4969 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
4970 if (sWidth == 0) { this.zeroWidthHack(); }
4971 this.checkedZeroWidth = true;
4972 }
4973
4974 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
4975 };
4976
4977 NativeScrollbars.prototype.setScrollLeft = function (pos) {
4978 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
4979 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
4980 };
4981
4982 NativeScrollbars.prototype.setScrollTop = function (pos) {
4983 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
4984 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
4985 };
4986
4987 NativeScrollbars.prototype.zeroWidthHack = function () {
4988 var w = mac && !mac_geMountainLion ? "12px" : "18px";
4989 this.horiz.style.height = this.vert.style.width = w;
4990 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
4991 this.disableHoriz = new Delayed;
4992 this.disableVert = new Delayed;
4993 };
4994
4995 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
4996 bar.style.pointerEvents = "auto";
4997 function maybeDisable() {
4998 // To find out whether the scrollbar is still visible, we
4999 // check whether the element under the pixel in the bottom
5000 // right corner of the scrollbar box is the scrollbar box
5001 // itself (when the bar is still visible) or its filler child
5002 // (when the bar is hidden). If it is still visible, we keep
5003 // it enabled, if it's hidden, we disable pointer events.
5004 var box = bar.getBoundingClientRect();
5005 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5006 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5007 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5008 else { delay.set(1000, maybeDisable); }
5009 }
5010 delay.set(1000, maybeDisable);
5011 };
5012
5013 NativeScrollbars.prototype.clear = function () {
5014 var parent = this.horiz.parentNode;
5015 parent.removeChild(this.horiz);
5016 parent.removeChild(this.vert);
5017 };
5018
5019 var NullScrollbars = function () {};
5020
5021 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5022 NullScrollbars.prototype.setScrollLeft = function () {};
5023 NullScrollbars.prototype.setScrollTop = function () {};
5024 NullScrollbars.prototype.clear = function () {};
5025
5026 function updateScrollbars(cm, measure) {
5027 if (!measure) { measure = measureForScrollbars(cm); }
5028 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5029 updateScrollbarsInner(cm, measure);
5030 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5031 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5032 { updateHeightsInViewport(cm); }
5033 updateScrollbarsInner(cm, measureForScrollbars(cm));
5034 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5035 }
5036 }
5037
5038 // Re-synchronize the fake scrollbars with the actual size of the
5039 // content.
5040 function updateScrollbarsInner(cm, measure) {
5041 var d = cm.display;
5042 var sizes = d.scrollbars.update(measure);
5043
5044 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5045 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5046 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5047
5048 if (sizes.right && sizes.bottom) {
5049 d.scrollbarFiller.style.display = "block";
5050 d.scrollbarFiller.style.height = sizes.bottom + "px";
5051 d.scrollbarFiller.style.width = sizes.right + "px";
5052 } else { d.scrollbarFiller.style.display = ""; }
5053 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5054 d.gutterFiller.style.display = "block";
5055 d.gutterFiller.style.height = sizes.bottom + "px";
5056 d.gutterFiller.style.width = measure.gutterWidth + "px";
5057 } else { d.gutterFiller.style.display = ""; }
5058 }
5059
5060 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5061
5062 function initScrollbars(cm) {
5063 if (cm.display.scrollbars) {
5064 cm.display.scrollbars.clear();
5065 if (cm.display.scrollbars.addClass)
5066 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5067 }
5068
5069 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5070 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5071 // Prevent clicks in the scrollbars from killing focus
5072 on(node, "mousedown", function () {
5073 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5074 });
5075 node.setAttribute("cm-not-content", "true");
5076 }, function (pos, axis) {
5077 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5078 else { updateScrollTop(cm, pos); }
5079 }, cm);
5080 if (cm.display.scrollbars.addClass)
5081 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5082 }
5083
5084 // Operations are used to wrap a series of changes to the editor
5085 // state in such a way that each change won't have to update the
5086 // cursor and display (which would be awkward, slow, and
5087 // error-prone). Instead, display updates are batched and then all
5088 // combined and executed at once.
5089
5090 var nextOpId = 0;
5091 // Start a new operation.
5092 function startOperation(cm) {
5093 cm.curOp = {
5094 cm: cm,
5095 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5096 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5097 forceUpdate: false, // Used to force a redraw
5098 updateInput: null, // Whether to reset the input textarea
5099 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5100 changeObjs: null, // Accumulated changes, for firing change events
5101 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5102 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5103 selectionChanged: false, // Whether the selection needs to be redrawn
5104 updateMaxLine: false, // Set when the widest line needs to be determined anew
5105 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5106 scrollToPos: null, // Used to scroll to a specific position
5107 focus: false,
5108 id: ++nextOpId // Unique ID
5109 };
5110 pushOperation(cm.curOp);
5111 }
5112
5113 // Finish an operation, updating the display and signalling delayed events
5114 function endOperation(cm) {
5115 var op = cm.curOp;
5116 finishOperation(op, function (group) {
5117 for (var i = 0; i < group.ops.length; i++)
5118 { group.ops[i].cm.curOp = null; }
5119 endOperations(group);
5120 });
5121 }
5122
5123 // The DOM updates done when an operation finishes are batched so
5124 // that the minimum number of relayouts are required.
5125 function endOperations(group) {
5126 var ops = group.ops;
5127 for (var i = 0; i < ops.length; i++) // Read DOM
5128 { endOperation_R1(ops[i]); }
5129 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5130 { endOperation_W1(ops[i$1]); }
5131 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5132 { endOperation_R2(ops[i$2]); }
5133 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5134 { endOperation_W2(ops[i$3]); }
5135 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5136 { endOperation_finish(ops[i$4]); }
5137 }
5138
5139 function endOperation_R1(op) {
5140 var cm = op.cm, display = cm.display;
5141 maybeClipScrollbars(cm);
5142 if (op.updateMaxLine) { findMaxLine(cm); }
5143
5144 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5145 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5146 op.scrollToPos.to.line >= display.viewTo) ||
5147 display.maxLineChanged && cm.options.lineWrapping;
5148 op.update = op.mustUpdate &&
5149 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5150 }
5151
5152 function endOperation_W1(op) {
5153 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5154 }
5155
5156 function endOperation_R2(op) {
5157 var cm = op.cm, display = cm.display;
5158 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5159
5160 op.barMeasure = measureForScrollbars(cm);
5161
5162 // If the max line changed since it was last measured, measure it,
5163 // and ensure the document's width matches it.
5164 // updateDisplay_W2 will use these properties to do the actual resizing
5165 if (display.maxLineChanged && !cm.options.lineWrapping) {
5166 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5167 cm.display.sizerWidth = op.adjustWidthTo;
5168 op.barMeasure.scrollWidth =
5169 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5170 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5171 }
5172
5173 if (op.updatedDisplay || op.selectionChanged)
5174 { op.preparedSelection = display.input.prepareSelection(op.focus); }
5175 }
5176
5177 function endOperation_W2(op) {
5178 var cm = op.cm;
5179
5180 if (op.adjustWidthTo != null) {
5181 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5182 if (op.maxScrollLeft < cm.doc.scrollLeft)
5183 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5184 cm.display.maxLineChanged = false;
5185 }
5186
5187 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus());
5188 if (op.preparedSelection)
5189 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5190 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5191 { updateScrollbars(cm, op.barMeasure); }
5192 if (op.updatedDisplay)
5193 { setDocumentHeight(cm, op.barMeasure); }
5194
5195 if (op.selectionChanged) { restartBlink(cm); }
5196
5197 if (cm.state.focused && op.updateInput)
5198 { cm.display.input.reset(op.typing); }
5199 if (takeFocus) { ensureFocus(op.cm); }
5200 }
5201
5202 function endOperation_finish(op) {
5203 var cm = op.cm, display = cm.display, doc = cm.doc;
5204
5205 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5206
5207 // Abort mouse wheel delta measurement, when scrolling explicitly
5208 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5209 { display.wheelStartX = display.wheelStartY = null; }
5210
5211 // Propagate the scroll position to the actual DOM scroller
5212 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5213
5214 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5215 // If we need to scroll a specific position into view, do so.
5216 if (op.scrollToPos) {
5217 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5218 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5219 maybeScrollWindow(cm, rect);
5220 }
5221
5222 // Fire events for markers that are hidden/unidden by editing or
5223 // undoing
5224 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5225 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5226 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5227 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5228 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5229
5230 if (display.wrapper.offsetHeight)
5231 { doc.scrollTop = cm.display.scroller.scrollTop; }
5232
5233 // Fire change events, and delayed event handlers
5234 if (op.changeObjs)
5235 { signal(cm, "changes", cm, op.changeObjs); }
5236 if (op.update)
5237 { op.update.finish(); }
5238 }
5239
5240 // Run the given function in an operation
5241 function runInOp(cm, f) {
5242 if (cm.curOp) { return f() }
5243 startOperation(cm);
5244 try { return f() }
5245 finally { endOperation(cm); }
5246 }
5247 // Wraps a function in an operation. Returns the wrapped function.
5248 function operation(cm, f) {
5249 return function() {
5250 if (cm.curOp) { return f.apply(cm, arguments) }
5251 startOperation(cm);
5252 try { return f.apply(cm, arguments) }
5253 finally { endOperation(cm); }
5254 }
5255 }
5256 // Used to add methods to editor and doc instances, wrapping them in
5257 // operations.
5258 function methodOp(f) {
5259 return function() {
5260 if (this.curOp) { return f.apply(this, arguments) }
5261 startOperation(this);
5262 try { return f.apply(this, arguments) }
5263 finally { endOperation(this); }
5264 }
5265 }
5266 function docMethodOp(f) {
5267 return function() {
5268 var cm = this.cm;
5269 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5270 startOperation(cm);
5271 try { return f.apply(this, arguments) }
5272 finally { endOperation(cm); }
5273 }
5274 }
5275
5276 // Updates the display.view data structure for a given change to the
5277 // document. From and to are in pre-change coordinates. Lendiff is
5278 // the amount of lines added or subtracted by the change. This is
5279 // used for changes that span multiple lines, or change the way
5280 // lines are divided into visual lines. regLineChange (below)
5281 // registers single-line changes.
5282 function regChange(cm, from, to, lendiff) {
5283 if (from == null) { from = cm.doc.first; }
5284 if (to == null) { to = cm.doc.first + cm.doc.size; }
5285 if (!lendiff) { lendiff = 0; }
5286
5287 var display = cm.display;
5288 if (lendiff && to < display.viewTo &&
5289 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5290 { display.updateLineNumbers = from; }
5291
5292 cm.curOp.viewChanged = true;
5293
5294 if (from >= display.viewTo) { // Change after
5295 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5296 { resetView(cm); }
5297 } else if (to <= display.viewFrom) { // Change before
5298 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5299 resetView(cm);
5300 } else {
5301 display.viewFrom += lendiff;
5302 display.viewTo += lendiff;
5303 }
5304 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5305 resetView(cm);
5306 } else if (from <= display.viewFrom) { // Top overlap
5307 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5308 if (cut) {
5309 display.view = display.view.slice(cut.index);
5310 display.viewFrom = cut.lineN;
5311 display.viewTo += lendiff;
5312 } else {
5313 resetView(cm);
5314 }
5315 } else if (to >= display.viewTo) { // Bottom overlap
5316 var cut$1 = viewCuttingPoint(cm, from, from, -1);
5317 if (cut$1) {
5318 display.view = display.view.slice(0, cut$1.index);
5319 display.viewTo = cut$1.lineN;
5320 } else {
5321 resetView(cm);
5322 }
5323 } else { // Gap in the middle
5324 var cutTop = viewCuttingPoint(cm, from, from, -1);
5325 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5326 if (cutTop && cutBot) {
5327 display.view = display.view.slice(0, cutTop.index)
5328 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5329 .concat(display.view.slice(cutBot.index));
5330 display.viewTo += lendiff;
5331 } else {
5332 resetView(cm);
5333 }
5334 }
5335
5336 var ext = display.externalMeasured;
5337 if (ext) {
5338 if (to < ext.lineN)
5339 { ext.lineN += lendiff; }
5340 else if (from < ext.lineN + ext.size)
5341 { display.externalMeasured = null; }
5342 }
5343 }
5344
5345 // Register a change to a single line. Type must be one of "text",
5346 // "gutter", "class", "widget"
5347 function regLineChange(cm, line, type) {
5348 cm.curOp.viewChanged = true;
5349 var display = cm.display, ext = cm.display.externalMeasured;
5350 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5351 { display.externalMeasured = null; }
5352
5353 if (line < display.viewFrom || line >= display.viewTo) { return }
5354 var lineView = display.view[findViewIndex(cm, line)];
5355 if (lineView.node == null) { return }
5356 var arr = lineView.changes || (lineView.changes = []);
5357 if (indexOf(arr, type) == -1) { arr.push(type); }
5358 }
5359
5360 // Clear the view.
5361 function resetView(cm) {
5362 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5363 cm.display.view = [];
5364 cm.display.viewOffset = 0;
5365 }
5366
5367 function viewCuttingPoint(cm, oldN, newN, dir) {
5368 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5369 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5370 { return {index: index, lineN: newN} }
5371 var n = cm.display.viewFrom;
5372 for (var i = 0; i < index; i++)
5373 { n += view[i].size; }
5374 if (n != oldN) {
5375 if (dir > 0) {
5376 if (index == view.length - 1) { return null }
5377 diff = (n + view[index].size) - oldN;
5378 index++;
5379 } else {
5380 diff = n - oldN;
5381 }
5382 oldN += diff; newN += diff;
5383 }
5384 while (visualLineNo(cm.doc, newN) != newN) {
5385 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
5386 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5387 index += dir;
5388 }
5389 return {index: index, lineN: newN}
5390 }
5391
5392 // Force the view to cover a given range, adding empty view element
5393 // or clipping off existing ones as needed.
5394 function adjustView(cm, from, to) {
5395 var display = cm.display, view = display.view;
5396 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5397 display.view = buildViewArray(cm, from, to);
5398 display.viewFrom = from;
5399 } else {
5400 if (display.viewFrom > from)
5401 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
5402 else if (display.viewFrom < from)
5403 { display.view = display.view.slice(findViewIndex(cm, from)); }
5404 display.viewFrom = from;
5405 if (display.viewTo < to)
5406 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
5407 else if (display.viewTo > to)
5408 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
5409 }
5410 display.viewTo = to;
5411 }
5412
5413 // Count the number of lines in the view whose DOM representation is
5414 // out of date (or nonexistent).
5415 function countDirtyView(cm) {
5416 var view = cm.display.view, dirty = 0;
5417 for (var i = 0; i < view.length; i++) {
5418 var lineView = view[i];
5419 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
5420 }
5421 return dirty
5422 }
5423
5424 // HIGHLIGHT WORKER
5425
5426 function startWorker(cm, time) {
5427 if (cm.doc.highlightFrontier < cm.display.viewTo)
5428 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5429 }
5430
5431 function highlightWorker(cm) {
5432 var doc = cm.doc;
5433 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5434 var end = +new Date + cm.options.workTime;
5435 var context = getContextBefore(cm, doc.highlightFrontier);
5436 var changedLines = [];
5437
5438 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5439 if (context.line >= cm.display.viewFrom) { // Visible
5440 var oldStyles = line.styles;
5441 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5442 var highlighted = highlightLine(cm, line, context, true);
5443 if (resetState) { context.state = resetState; }
5444 line.styles = highlighted.styles;
5445 var oldCls = line.styleClasses, newCls = highlighted.classes;
5446 if (newCls) { line.styleClasses = newCls; }
5447 else if (oldCls) { line.styleClasses = null; }
5448 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5449 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5450 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5451 if (ischange) { changedLines.push(context.line); }
5452 line.stateAfter = context.save();
5453 context.nextLine();
5454 } else {
5455 if (line.text.length <= cm.options.maxHighlightLength)
5456 { processLine(cm, line.text, context); }
5457 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5458 context.nextLine();
5459 }
5460 if (+new Date > end) {
5461 startWorker(cm, cm.options.workDelay);
5462 return true
5463 }
5464 });
5465 doc.highlightFrontier = context.line;
5466 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5467 if (changedLines.length) { runInOp(cm, function () {
5468 for (var i = 0; i < changedLines.length; i++)
5469 { regLineChange(cm, changedLines[i], "text"); }
5470 }); }
5471 }
5472
5473 // DISPLAY DRAWING
5474
5475 var DisplayUpdate = function(cm, viewport, force) {
5476 var display = cm.display;
5477
5478 this.viewport = viewport;
5479 // Store some values that we'll need later (but don't want to force a relayout for)
5480 this.visible = visibleLines(display, cm.doc, viewport);
5481 this.editorIsHidden = !display.wrapper.offsetWidth;
5482 this.wrapperHeight = display.wrapper.clientHeight;
5483 this.wrapperWidth = display.wrapper.clientWidth;
5484 this.oldDisplayWidth = displayWidth(cm);
5485 this.force = force;
5486 this.dims = getDimensions(cm);
5487 this.events = [];
5488 };
5489
5490 DisplayUpdate.prototype.signal = function (emitter, type) {
5491 if (hasHandler(emitter, type))
5492 { this.events.push(arguments); }
5493 };
5494 DisplayUpdate.prototype.finish = function () {
5495 var this$1 = this;
5496
5497 for (var i = 0; i < this.events.length; i++)
5498 { signal.apply(null, this$1.events[i]); }
5499 };
5500
5501 function maybeClipScrollbars(cm) {
5502 var display = cm.display;
5503 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5504 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5505 display.heightForcer.style.height = scrollGap(cm) + "px";
5506 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5507 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5508 display.scrollbarsClipped = true;
5509 }
5510 }
5511
5512 function selectionSnapshot(cm) {
5513 if (cm.hasFocus()) { return null }
5514 var active = activeElt();
5515 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5516 var result = {activeElt: active};
5517 if (window.getSelection) {
5518 var sel = window.getSelection();
5519 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5520 result.anchorNode = sel.anchorNode;
5521 result.anchorOffset = sel.anchorOffset;
5522 result.focusNode = sel.focusNode;
5523 result.focusOffset = sel.focusOffset;
5524 }
5525 }
5526 return result
5527 }
5528
5529 function restoreSelection(snapshot) {
5530 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5531 snapshot.activeElt.focus();
5532 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5533 var sel = window.getSelection(), range$$1 = document.createRange();
5534 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5535 range$$1.collapse(false);
5536 sel.removeAllRanges();
5537 sel.addRange(range$$1);
5538 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5539 }
5540 }
5541
5542 // Does the actual updating of the line display. Bails out
5543 // (returning false) when there is nothing to be done and forced is
5544 // false.
5545 function updateDisplayIfNeeded(cm, update) {
5546 var display = cm.display, doc = cm.doc;
5547
5548 if (update.editorIsHidden) {
5549 resetView(cm);
5550 return false
5551 }
5552
5553 // Bail out if the visible area is already rendered and nothing changed.
5554 if (!update.force &&
5555 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5556 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5557 display.renderedView == display.view && countDirtyView(cm) == 0)
5558 { return false }
5559
5560 if (maybeUpdateLineNumberWidth(cm)) {
5561 resetView(cm);
5562 update.dims = getDimensions(cm);
5563 }
5564
5565 // Compute a suitable new viewport (from & to)
5566 var end = doc.first + doc.size;
5567 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5568 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5569 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5570 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5571 if (sawCollapsedSpans) {
5572 from = visualLineNo(cm.doc, from);
5573 to = visualLineEndNo(cm.doc, to);
5574 }
5575
5576 var different = from != display.viewFrom || to != display.viewTo ||
5577 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5578 adjustView(cm, from, to);
5579
5580 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5581 // Position the mover div to align with the current scroll position
5582 cm.display.mover.style.top = display.viewOffset + "px";
5583
5584 var toUpdate = countDirtyView(cm);
5585 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5586 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5587 { return false }
5588
5589 // For big changes, we hide the enclosing element during the
5590 // update, since that speeds up the operations on most browsers.
5591 var selSnapshot = selectionSnapshot(cm);
5592 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5593 patchDisplay(cm, display.updateLineNumbers, update.dims);
5594 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5595 display.renderedView = display.view;
5596 // There might have been a widget with a focused element that got
5597 // hidden or updated, if so re-focus it.
5598 restoreSelection(selSnapshot);
5599
5600 // Prevent selection and cursors from interfering with the scroll
5601 // width and height.
5602 removeChildren(display.cursorDiv);
5603 removeChildren(display.selectionDiv);
5604 display.gutters.style.height = display.sizer.style.minHeight = 0;
5605
5606 if (different) {
5607 display.lastWrapHeight = update.wrapperHeight;
5608 display.lastWrapWidth = update.wrapperWidth;
5609 startWorker(cm, 400);
5610 }
5611
5612 display.updateLineNumbers = null;
5613
5614 return true
5615 }
5616
5617 function postUpdateDisplay(cm, update) {
5618 var viewport = update.viewport;
5619
5620 for (var first = true;; first = false) {
5621 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5622 // Clip forced viewport to actual scrollable area.
5623 if (viewport && viewport.top != null)
5624 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5625 // Updated line heights might result in the drawn area not
5626 // actually covering the viewport. Keep looping until it does.
5627 update.visible = visibleLines(cm.display, cm.doc, viewport);
5628 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5629 { break }
5630 }
5631 if (!updateDisplayIfNeeded(cm, update)) { break }
5632 updateHeightsInViewport(cm);
5633 var barMeasure = measureForScrollbars(cm);
5634 updateSelection(cm);
5635 updateScrollbars(cm, barMeasure);
5636 setDocumentHeight(cm, barMeasure);
5637 update.force = false;
5638 }
5639
5640 update.signal(cm, "update", cm);
5641 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5642 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5643 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5644 }
5645 }
5646
5647 function updateDisplaySimple(cm, viewport) {
5648 var update = new DisplayUpdate(cm, viewport);
5649 if (updateDisplayIfNeeded(cm, update)) {
5650 updateHeightsInViewport(cm);
5651 postUpdateDisplay(cm, update);
5652 var barMeasure = measureForScrollbars(cm);
5653 updateSelection(cm);
5654 updateScrollbars(cm, barMeasure);
5655 setDocumentHeight(cm, barMeasure);
5656 update.finish();
5657 }
5658 }
5659
5660 // Sync the actual display DOM structure with display.view, removing
5661 // nodes for lines that are no longer in view, and creating the ones
5662 // that are not there yet, and updating the ones that are out of
5663 // date.
5664 function patchDisplay(cm, updateNumbersFrom, dims) {
5665 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5666 var container = display.lineDiv, cur = container.firstChild;
5667
5668 function rm(node) {
5669 var next = node.nextSibling;
5670 // Works around a throw-scroll bug in OS X Webkit
5671 if (webkit && mac && cm.display.currentWheelTarget == node)
5672 { node.style.display = "none"; }
5673 else
5674 { node.parentNode.removeChild(node); }
5675 return next
5676 }
5677
5678 var view = display.view, lineN = display.viewFrom;
5679 // Loop over the elements in the view, syncing cur (the DOM nodes
5680 // in display.lineDiv) with the view as we go.
5681 for (var i = 0; i < view.length; i++) {
5682 var lineView = view[i];
5683 if (lineView.hidden) {
5684 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5685 var node = buildLineElement(cm, lineView, lineN, dims);
5686 container.insertBefore(node, cur);
5687 } else { // Already drawn
5688 while (cur != lineView.node) { cur = rm(cur); }
5689 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5690 updateNumbersFrom <= lineN && lineView.lineNumber;
5691 if (lineView.changes) {
5692 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5693 updateLineForChanges(cm, lineView, lineN, dims);
5694 }
5695 if (updateNumber) {
5696 removeChildren(lineView.lineNumber);
5697 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5698 }
5699 cur = lineView.node.nextSibling;
5700 }
5701 lineN += lineView.size;
5702 }
5703 while (cur) { cur = rm(cur); }
5704 }
5705
5706 function updateGutterSpace(cm) {
5707 var width = cm.display.gutters.offsetWidth;
5708 cm.display.sizer.style.marginLeft = width + "px";
5709 }
5710
5711 function setDocumentHeight(cm, measure) {
5712 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5713 cm.display.heightForcer.style.top = measure.docHeight + "px";
5714 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5715 }
5716
5717 // Rebuild the gutter elements, ensure the margin to the left of the
5718 // code matches their width.
5719 function updateGutters(cm) {
5720 var gutters = cm.display.gutters, specs = cm.options.gutters;
5721 removeChildren(gutters);
5722 var i = 0;
5723 for (; i < specs.length; ++i) {
5724 var gutterClass = specs[i];
5725 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
5726 if (gutterClass == "CodeMirror-linenumbers") {
5727 cm.display.lineGutter = gElt;
5728 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
5729 }
5730 }
5731 gutters.style.display = i ? "" : "none";
5732 updateGutterSpace(cm);
5733 }
5734
5735 // Make sure the gutters options contains the element
5736 // "CodeMirror-linenumbers" when the lineNumbers option is true.
5737 function setGuttersForLineNumbers(options) {
5738 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
5739 if (found == -1 && options.lineNumbers) {
5740 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
5741 } else if (found > -1 && !options.lineNumbers) {
5742 options.gutters = options.gutters.slice(0);
5743 options.gutters.splice(found, 1);
5744 }
5745 }
5746
5747 // Since the delta values reported on mouse wheel events are
5748 // unstandardized between browsers and even browser versions, and
5749 // generally horribly unpredictable, this code starts by measuring
5750 // the scroll effect that the first few mouse wheel events have,
5751 // and, from that, detects the way it can convert deltas to pixel
5752 // offsets afterwards.
5753 //
5754 // The reason we want to know the amount a wheel event will scroll
5755 // is that it gives us a chance to update the display before the
5756 // actual scrolling happens, reducing flickering.
5757
5758 var wheelSamples = 0;
5759 var wheelPixelsPerUnit = null;
5760 // Fill in a browser-detected starting value on browsers where we
5761 // know one. These don't have to be accurate -- the result of them
5762 // being wrong would just be a slight flicker on the first wheel
5763 // scroll (if it is large enough).
5764 if (ie) { wheelPixelsPerUnit = -.53; }
5765 else if (gecko) { wheelPixelsPerUnit = 15; }
5766 else if (chrome) { wheelPixelsPerUnit = -.7; }
5767 else if (safari) { wheelPixelsPerUnit = -1/3; }
5768
5769 function wheelEventDelta(e) {
5770 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
5771 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
5772 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
5773 else if (dy == null) { dy = e.wheelDelta; }
5774 return {x: dx, y: dy}
5775 }
5776 function wheelEventPixels(e) {
5777 var delta = wheelEventDelta(e);
5778 delta.x *= wheelPixelsPerUnit;
5779 delta.y *= wheelPixelsPerUnit;
5780 return delta
5781 }
5782
5783 function onScrollWheel(cm, e) {
5784 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
5785
5786 var display = cm.display, scroll = display.scroller;
5787 // Quit if there's nothing to scroll here
5788 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
5789 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
5790 if (!(dx && canScrollX || dy && canScrollY)) { return }
5791
5792 // Webkit browsers on OS X abort momentum scrolls when the target
5793 // of the scroll event is removed from the scrollable element.
5794 // This hack (see related code in patchDisplay) makes sure the
5795 // element is kept around.
5796 if (dy && mac && webkit) {
5797 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
5798 for (var i = 0; i < view.length; i++) {
5799 if (view[i].node == cur) {
5800 cm.display.currentWheelTarget = cur;
5801 break outer
5802 }
5803 }
5804 }
5805 }
5806
5807 // On some browsers, horizontal scrolling will cause redraws to
5808 // happen before the gutter has been realigned, causing it to
5809 // wriggle around in a most unseemly way. When we have an
5810 // estimated pixels/delta value, we just handle horizontal
5811 // scrolling entirely here. It'll be slightly off from native, but
5812 // better than glitching out.
5813 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
5814 if (dy && canScrollY)
5815 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
5816 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
5817 // Only prevent default scrolling if vertical scrolling is
5818 // actually possible. Otherwise, it causes vertical scroll
5819 // jitter on OSX trackpads when deltaX is small and deltaY
5820 // is large (issue #3579)
5821 if (!dy || (dy && canScrollY))
5822 { e_preventDefault(e); }
5823 display.wheelStartX = null; // Abort measurement, if in progress
5824 return
5825 }
5826
5827 // 'Project' the visible viewport to cover the area that is being
5828 // scrolled into view (if we know enough to estimate it).
5829 if (dy && wheelPixelsPerUnit != null) {
5830 var pixels = dy * wheelPixelsPerUnit;
5831 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
5832 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
5833 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
5834 updateDisplaySimple(cm, {top: top, bottom: bot});
5835 }
5836
5837 if (wheelSamples < 20) {
5838 if (display.wheelStartX == null) {
5839 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
5840 display.wheelDX = dx; display.wheelDY = dy;
5841 setTimeout(function () {
5842 if (display.wheelStartX == null) { return }
5843 var movedX = scroll.scrollLeft - display.wheelStartX;
5844 var movedY = scroll.scrollTop - display.wheelStartY;
5845 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
5846 (movedX && display.wheelDX && movedX / display.wheelDX);
5847 display.wheelStartX = display.wheelStartY = null;
5848 if (!sample) { return }
5849 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
5850 ++wheelSamples;
5851 }, 200);
5852 } else {
5853 display.wheelDX += dx; display.wheelDY += dy;
5854 }
5855 }
5856 }
5857
5858 // Selection objects are immutable. A new one is created every time
5859 // the selection changes. A selection is one or more non-overlapping
5860 // (and non-touching) ranges, sorted, and an integer that indicates
5861 // which one is the primary selection (the one that's scrolled into
5862 // view, that getCursor returns, etc).
5863 var Selection = function(ranges, primIndex) {
5864 this.ranges = ranges;
5865 this.primIndex = primIndex;
5866 };
5867
5868 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
5869
5870 Selection.prototype.equals = function (other) {
5871 var this$1 = this;
5872
5873 if (other == this) { return true }
5874 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
5875 for (var i = 0; i < this.ranges.length; i++) {
5876 var here = this$1.ranges[i], there = other.ranges[i];
5877 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
5878 }
5879 return true
5880 };
5881
5882 Selection.prototype.deepCopy = function () {
5883 var this$1 = this;
5884
5885 var out = [];
5886 for (var i = 0; i < this.ranges.length; i++)
5887 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
5888 return new Selection(out, this.primIndex)
5889 };
5890
5891 Selection.prototype.somethingSelected = function () {
5892 var this$1 = this;
5893
5894 for (var i = 0; i < this.ranges.length; i++)
5895 { if (!this$1.ranges[i].empty()) { return true } }
5896 return false
5897 };
5898
5899 Selection.prototype.contains = function (pos, end) {
5900 var this$1 = this;
5901
5902 if (!end) { end = pos; }
5903 for (var i = 0; i < this.ranges.length; i++) {
5904 var range = this$1.ranges[i];
5905 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
5906 { return i }
5907 }
5908 return -1
5909 };
5910
5911 var Range = function(anchor, head) {
5912 this.anchor = anchor; this.head = head;
5913 };
5914
5915 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
5916 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
5917 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
5918
5919 // Take an unsorted, potentially overlapping set of ranges, and
5920 // build a selection out of it. 'Consumes' ranges array (modifying
5921 // it).
5922 function normalizeSelection(ranges, primIndex) {
5923 var prim = ranges[primIndex];
5924 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
5925 primIndex = indexOf(ranges, prim);
5926 for (var i = 1; i < ranges.length; i++) {
5927 var cur = ranges[i], prev = ranges[i - 1];
5928 if (cmp(prev.to(), cur.from()) >= 0) {
5929 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
5930 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
5931 if (i <= primIndex) { --primIndex; }
5932 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
5933 }
5934 }
5935 return new Selection(ranges, primIndex)
5936 }
5937
5938 function simpleSelection(anchor, head) {
5939 return new Selection([new Range(anchor, head || anchor)], 0)
5940 }
5941
5942 // Compute the position of the end of a change (its 'to' property
5943 // refers to the pre-change end).
5944 function changeEnd(change) {
5945 if (!change.text) { return change.to }
5946 return Pos(change.from.line + change.text.length - 1,
5947 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
5948 }
5949
5950 // Adjust a position to refer to the post-change position of the
5951 // same text, or the end of the change if the change covers it.
5952 function adjustForChange(pos, change) {
5953 if (cmp(pos, change.from) < 0) { return pos }
5954 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
5955
5956 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
5957 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
5958 return Pos(line, ch)
5959 }
5960
5961 function computeSelAfterChange(doc, change) {
5962 var out = [];
5963 for (var i = 0; i < doc.sel.ranges.length; i++) {
5964 var range = doc.sel.ranges[i];
5965 out.push(new Range(adjustForChange(range.anchor, change),
5966 adjustForChange(range.head, change)));
5967 }
5968 return normalizeSelection(out, doc.sel.primIndex)
5969 }
5970
5971 function offsetPos(pos, old, nw) {
5972 if (pos.line == old.line)
5973 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
5974 else
5975 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
5976 }
5977
5978 // Used by replaceSelections to allow moving the selection to the
5979 // start or around the replaced test. Hint may be "start" or "around".
5980 function computeReplacedSel(doc, changes, hint) {
5981 var out = [];
5982 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
5983 for (var i = 0; i < changes.length; i++) {
5984 var change = changes[i];
5985 var from = offsetPos(change.from, oldPrev, newPrev);
5986 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
5987 oldPrev = change.to;
5988 newPrev = to;
5989 if (hint == "around") {
5990 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
5991 out[i] = new Range(inv ? to : from, inv ? from : to);
5992 } else {
5993 out[i] = new Range(from, from);
5994 }
5995 }
5996 return new Selection(out, doc.sel.primIndex)
5997 }
5998
5999 // Used to get the editor into a consistent state again when options change.
6000
6001 function loadMode(cm) {
6002 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6003 resetModeState(cm);
6004 }
6005
6006 function resetModeState(cm) {
6007 cm.doc.iter(function (line) {
6008 if (line.stateAfter) { line.stateAfter = null; }
6009 if (line.styles) { line.styles = null; }
6010 });
6011 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6012 startWorker(cm, 100);
6013 cm.state.modeGen++;
6014 if (cm.curOp) { regChange(cm); }
6015 }
6016
6017 // DOCUMENT DATA STRUCTURE
6018
6019 // By default, updates that start and end at the beginning of a line
6020 // are treated specially, in order to make the association of line
6021 // widgets and marker elements with the text behave more intuitive.
6022 function isWholeLineUpdate(doc, change) {
6023 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6024 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6025 }
6026
6027 // Perform a change on the document data structure.
6028 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6029 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6030 function update(line, text, spans) {
6031 updateLine(line, text, spans, estimateHeight$$1);
6032 signalLater(line, "change", line, change);
6033 }
6034 function linesFor(start, end) {
6035 var result = [];
6036 for (var i = start; i < end; ++i)
6037 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6038 return result
6039 }
6040
6041 var from = change.from, to = change.to, text = change.text;
6042 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6043 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6044
6045 // Adjust the line structure
6046 if (change.full) {
6047 doc.insert(0, linesFor(0, text.length));
6048 doc.remove(text.length, doc.size - text.length);
6049 } else if (isWholeLineUpdate(doc, change)) {
6050 // This is a whole-line replace. Treated specially to make
6051 // sure line objects move the way they are supposed to.
6052 var added = linesFor(0, text.length - 1);
6053 update(lastLine, lastLine.text, lastSpans);
6054 if (nlines) { doc.remove(from.line, nlines); }
6055 if (added.length) { doc.insert(from.line, added); }
6056 } else if (firstLine == lastLine) {
6057 if (text.length == 1) {
6058 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6059 } else {
6060 var added$1 = linesFor(1, text.length - 1);
6061 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6062 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6063 doc.insert(from.line + 1, added$1);
6064 }
6065 } else if (text.length == 1) {
6066 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6067 doc.remove(from.line + 1, nlines);
6068 } else {
6069 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6070 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6071 var added$2 = linesFor(1, text.length - 1);
6072 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6073 doc.insert(from.line + 1, added$2);
6074 }
6075
6076 signalLater(doc, "change", doc, change);
6077 }
6078
6079 // Call f for all linked documents.
6080 function linkedDocs(doc, f, sharedHistOnly) {
6081 function propagate(doc, skip, sharedHist) {
6082 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6083 var rel = doc.linked[i];
6084 if (rel.doc == skip) { continue }
6085 var shared = sharedHist && rel.sharedHist;
6086 if (sharedHistOnly && !shared) { continue }
6087 f(rel.doc, shared);
6088 propagate(rel.doc, doc, shared);
6089 } }
6090 }
6091 propagate(doc, null, true);
6092 }
6093
6094 // Attach a document to an editor.
6095 function attachDoc(cm, doc) {
6096 if (doc.cm) { throw new Error("This document is already in use.") }
6097 cm.doc = doc;
6098 doc.cm = cm;
6099 estimateLineHeights(cm);
6100 loadMode(cm);
6101 setDirectionClass(cm);
6102 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6103 cm.options.mode = doc.modeOption;
6104 regChange(cm);
6105 }
6106
6107 function setDirectionClass(cm) {
6108 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6109 }
6110
6111 function directionChanged(cm) {
6112 runInOp(cm, function () {
6113 setDirectionClass(cm);
6114 regChange(cm);
6115 });
6116 }
6117
6118 function History(startGen) {
6119 // Arrays of change events and selections. Doing something adds an
6120 // event to done and clears undo. Undoing moves events from done
6121 // to undone, redoing moves them in the other direction.
6122 this.done = []; this.undone = [];
6123 this.undoDepth = Infinity;
6124 // Used to track when changes can be merged into a single undo
6125 // event
6126 this.lastModTime = this.lastSelTime = 0;
6127 this.lastOp = this.lastSelOp = null;
6128 this.lastOrigin = this.lastSelOrigin = null;
6129 // Used by the isClean() method
6130 this.generation = this.maxGeneration = startGen || 1;
6131 }
6132
6133 // Create a history change event from an updateDoc-style change
6134 // object.
6135 function historyChangeFromChange(doc, change) {
6136 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6137 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6138 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6139 return histChange
6140 }
6141
6142 // Pop all selection events off the end of a history array. Stop at
6143 // a change event.
6144 function clearSelectionEvents(array) {
6145 while (array.length) {
6146 var last = lst(array);
6147 if (last.ranges) { array.pop(); }
6148 else { break }
6149 }
6150 }
6151
6152 // Find the top change event in the history. Pop off selection
6153 // events that are in the way.
6154 function lastChangeEvent(hist, force) {
6155 if (force) {
6156 clearSelectionEvents(hist.done);
6157 return lst(hist.done)
6158 } else if (hist.done.length && !lst(hist.done).ranges) {
6159 return lst(hist.done)
6160 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6161 hist.done.pop();
6162 return lst(hist.done)
6163 }
6164 }
6165
6166 // Register a change in the history. Merges changes that are within
6167 // a single operation, or are close together with an origin that
6168 // allows merging (starting with "+") into a single event.
6169 function addChangeToHistory(doc, change, selAfter, opId) {
6170 var hist = doc.history;
6171 hist.undone.length = 0;
6172 var time = +new Date, cur;
6173 var last;
6174
6175 if ((hist.lastOp == opId ||
6176 hist.lastOrigin == change.origin && change.origin &&
6177 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
6178 change.origin.charAt(0) == "*")) &&
6179 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6180 // Merge this change into the last event
6181 last = lst(cur.changes);
6182 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6183 // Optimized case for simple insertion -- don't want to add
6184 // new changesets for every character typed
6185 last.to = changeEnd(change);
6186 } else {
6187 // Add new sub-event
6188 cur.changes.push(historyChangeFromChange(doc, change));
6189 }
6190 } else {
6191 // Can not be merged, start a new event.
6192 var before = lst(hist.done);
6193 if (!before || !before.ranges)
6194 { pushSelectionToHistory(doc.sel, hist.done); }
6195 cur = {changes: [historyChangeFromChange(doc, change)],
6196 generation: hist.generation};
6197 hist.done.push(cur);
6198 while (hist.done.length > hist.undoDepth) {
6199 hist.done.shift();
6200 if (!hist.done[0].ranges) { hist.done.shift(); }
6201 }
6202 }
6203 hist.done.push(selAfter);
6204 hist.generation = ++hist.maxGeneration;
6205 hist.lastModTime = hist.lastSelTime = time;
6206 hist.lastOp = hist.lastSelOp = opId;
6207 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6208
6209 if (!last) { signal(doc, "historyAdded"); }
6210 }
6211
6212 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6213 var ch = origin.charAt(0);
6214 return ch == "*" ||
6215 ch == "+" &&
6216 prev.ranges.length == sel.ranges.length &&
6217 prev.somethingSelected() == sel.somethingSelected() &&
6218 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6219 }
6220
6221 // Called whenever the selection changes, sets the new selection as
6222 // the pending selection in the history, and pushes the old pending
6223 // selection into the 'done' array when it was significantly
6224 // different (in number of selected ranges, emptiness, or time).
6225 function addSelectionToHistory(doc, sel, opId, options) {
6226 var hist = doc.history, origin = options && options.origin;
6227
6228 // A new event is started when the previous origin does not match
6229 // the current, or the origins don't allow matching. Origins
6230 // starting with * are always merged, those starting with + are
6231 // merged when similar and close together in time.
6232 if (opId == hist.lastSelOp ||
6233 (origin && hist.lastSelOrigin == origin &&
6234 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6235 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6236 { hist.done[hist.done.length - 1] = sel; }
6237 else
6238 { pushSelectionToHistory(sel, hist.done); }
6239
6240 hist.lastSelTime = +new Date;
6241 hist.lastSelOrigin = origin;
6242 hist.lastSelOp = opId;
6243 if (options && options.clearRedo !== false)
6244 { clearSelectionEvents(hist.undone); }
6245 }
6246
6247 function pushSelectionToHistory(sel, dest) {
6248 var top = lst(dest);
6249 if (!(top && top.ranges && top.equals(sel)))
6250 { dest.push(sel); }
6251 }
6252
6253 // Used to store marked span information in the history.
6254 function attachLocalSpans(doc, change, from, to) {
6255 var existing = change["spans_" + doc.id], n = 0;
6256 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6257 if (line.markedSpans)
6258 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6259 ++n;
6260 });
6261 }
6262
6263 // When un/re-doing restores text containing marked spans, those
6264 // that have been explicitly cleared should not be restored.
6265 function removeClearedSpans(spans) {
6266 if (!spans) { return null }
6267 var out;
6268 for (var i = 0; i < spans.length; ++i) {
6269 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6270 else if (out) { out.push(spans[i]); }
6271 }
6272 return !out ? spans : out.length ? out : null
6273 }
6274
6275 // Retrieve and filter the old marked spans stored in a change event.
6276 function getOldSpans(doc, change) {
6277 var found = change["spans_" + doc.id];
6278 if (!found) { return null }
6279 var nw = [];
6280 for (var i = 0; i < change.text.length; ++i)
6281 { nw.push(removeClearedSpans(found[i])); }
6282 return nw
6283 }
6284
6285 // Used for un/re-doing changes from the history. Combines the
6286 // result of computing the existing spans with the set of spans that
6287 // existed in the history (so that deleting around a span and then
6288 // undoing brings back the span).
6289 function mergeOldSpans(doc, change) {
6290 var old = getOldSpans(doc, change);
6291 var stretched = stretchSpansOverChange(doc, change);
6292 if (!old) { return stretched }
6293 if (!stretched) { return old }
6294
6295 for (var i = 0; i < old.length; ++i) {
6296 var oldCur = old[i], stretchCur = stretched[i];
6297 if (oldCur && stretchCur) {
6298 spans: for (var j = 0; j < stretchCur.length; ++j) {
6299 var span = stretchCur[j];
6300 for (var k = 0; k < oldCur.length; ++k)
6301 { if (oldCur[k].marker == span.marker) { continue spans } }
6302 oldCur.push(span);
6303 }
6304 } else if (stretchCur) {
6305 old[i] = stretchCur;
6306 }
6307 }
6308 return old
6309 }
6310
6311 // Used both to provide a JSON-safe object in .getHistory, and, when
6312 // detaching a document, to split the history in two
6313 function copyHistoryArray(events, newGroup, instantiateSel) {
6314 var copy = [];
6315 for (var i = 0; i < events.length; ++i) {
6316 var event = events[i];
6317 if (event.ranges) {
6318 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6319 continue
6320 }
6321 var changes = event.changes, newChanges = [];
6322 copy.push({changes: newChanges});
6323 for (var j = 0; j < changes.length; ++j) {
6324 var change = changes[j], m = (void 0);
6325 newChanges.push({from: change.from, to: change.to, text: change.text});
6326 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6327 if (indexOf(newGroup, Number(m[1])) > -1) {
6328 lst(newChanges)[prop] = change[prop];
6329 delete change[prop];
6330 }
6331 } } }
6332 }
6333 }
6334 return copy
6335 }
6336
6337 // The 'scroll' parameter given to many of these indicated whether
6338 // the new cursor position should be scrolled into view after
6339 // modifying the selection.
6340
6341 // If shift is held or the extend flag is set, extends a range to
6342 // include a given position (and optionally a second position).
6343 // Otherwise, simply returns the range between the given positions.
6344 // Used for cursor motion and such.
6345 function extendRange(range, head, other, extend) {
6346 if (extend) {
6347 var anchor = range.anchor;
6348 if (other) {
6349 var posBefore = cmp(head, anchor) < 0;
6350 if (posBefore != (cmp(other, anchor) < 0)) {
6351 anchor = head;
6352 head = other;
6353 } else if (posBefore != (cmp(head, other) < 0)) {
6354 head = other;
6355 }
6356 }
6357 return new Range(anchor, head)
6358 } else {
6359 return new Range(other || head, head)
6360 }
6361 }
6362
6363 // Extend the primary selection range, discard the rest.
6364 function extendSelection(doc, head, other, options, extend) {
6365 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6366 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6367 }
6368
6369 // Extend all selections (pos is an array of selections with length
6370 // equal the number of selections)
6371 function extendSelections(doc, heads, options) {
6372 var out = [];
6373 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6374 for (var i = 0; i < doc.sel.ranges.length; i++)
6375 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6376 var newSel = normalizeSelection(out, doc.sel.primIndex);
6377 setSelection(doc, newSel, options);
6378 }
6379
6380 // Updates a single range in the selection.
6381 function replaceOneSelection(doc, i, range, options) {
6382 var ranges = doc.sel.ranges.slice(0);
6383 ranges[i] = range;
6384 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
6385 }
6386
6387 // Reset the selection to a single range.
6388 function setSimpleSelection(doc, anchor, head, options) {
6389 setSelection(doc, simpleSelection(anchor, head), options);
6390 }
6391
6392 // Give beforeSelectionChange handlers a change to influence a
6393 // selection update.
6394 function filterSelectionChange(doc, sel, options) {
6395 var obj = {
6396 ranges: sel.ranges,
6397 update: function(ranges) {
6398 var this$1 = this;
6399
6400 this.ranges = [];
6401 for (var i = 0; i < ranges.length; i++)
6402 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6403 clipPos(doc, ranges[i].head)); }
6404 },
6405 origin: options && options.origin
6406 };
6407 signal(doc, "beforeSelectionChange", doc, obj);
6408 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6409 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
6410 else { return sel }
6411 }
6412
6413 function setSelectionReplaceHistory(doc, sel, options) {
6414 var done = doc.history.done, last = lst(done);
6415 if (last && last.ranges) {
6416 done[done.length - 1] = sel;
6417 setSelectionNoUndo(doc, sel, options);
6418 } else {
6419 setSelection(doc, sel, options);
6420 }
6421 }
6422
6423 // Set a new selection.
6424 function setSelection(doc, sel, options) {
6425 setSelectionNoUndo(doc, sel, options);
6426 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6427 }
6428
6429 function setSelectionNoUndo(doc, sel, options) {
6430 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6431 { sel = filterSelectionChange(doc, sel, options); }
6432
6433 var bias = options && options.bias ||
6434 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6435 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6436
6437 if (!(options && options.scroll === false) && doc.cm)
6438 { ensureCursorVisible(doc.cm); }
6439 }
6440
6441 function setSelectionInner(doc, sel) {
6442 if (sel.equals(doc.sel)) { return }
6443
6444 doc.sel = sel;
6445
6446 if (doc.cm) {
6447 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
6448 signalCursorActivity(doc.cm);
6449 }
6450 signalLater(doc, "cursorActivity", doc);
6451 }
6452
6453 // Verify that the selection does not partially select any atomic
6454 // marked ranges.
6455 function reCheckSelection(doc) {
6456 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6457 }
6458
6459 // Return a selection that does not partially select any atomic
6460 // ranges.
6461 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6462 var out;
6463 for (var i = 0; i < sel.ranges.length; i++) {
6464 var range = sel.ranges[i];
6465 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6466 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6467 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6468 if (out || newAnchor != range.anchor || newHead != range.head) {
6469 if (!out) { out = sel.ranges.slice(0, i); }
6470 out[i] = new Range(newAnchor, newHead);
6471 }
6472 }
6473 return out ? normalizeSelection(out, sel.primIndex) : sel
6474 }
6475
6476 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6477 var line = getLine(doc, pos.line);
6478 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6479 var sp = line.markedSpans[i], m = sp.marker;
6480 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6481 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6482 if (mayClear) {
6483 signal(m, "beforeCursorEnter");
6484 if (m.explicitlyCleared) {
6485 if (!line.markedSpans) { break }
6486 else {--i; continue}
6487 }
6488 }
6489 if (!m.atomic) { continue }
6490
6491 if (oldPos) {
6492 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6493 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6494 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6495 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6496 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6497 }
6498
6499 var far = m.find(dir < 0 ? -1 : 1);
6500 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6501 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6502 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6503 }
6504 } }
6505 return pos
6506 }
6507
6508 // Ensure a given position is not inside an atomic range.
6509 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6510 var dir = bias || 1;
6511 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6512 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6513 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6514 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6515 if (!found) {
6516 doc.cantEdit = true;
6517 return Pos(doc.first, 0)
6518 }
6519 return found
6520 }
6521
6522 function movePos(doc, pos, dir, line) {
6523 if (dir < 0 && pos.ch == 0) {
6524 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6525 else { return null }
6526 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6527 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6528 else { return null }
6529 } else {
6530 return new Pos(pos.line, pos.ch + dir)
6531 }
6532 }
6533
6534 function selectAll(cm) {
6535 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6536 }
6537
6538 // UPDATING
6539
6540 // Allow "beforeChange" event handlers to influence a change
6541 function filterChange(doc, change, update) {
6542 var obj = {
6543 canceled: false,
6544 from: change.from,
6545 to: change.to,
6546 text: change.text,
6547 origin: change.origin,
6548 cancel: function () { return obj.canceled = true; }
6549 };
6550 if (update) { obj.update = function (from, to, text, origin) {
6551 if (from) { obj.from = clipPos(doc, from); }
6552 if (to) { obj.to = clipPos(doc, to); }
6553 if (text) { obj.text = text; }
6554 if (origin !== undefined) { obj.origin = origin; }
6555 }; }
6556 signal(doc, "beforeChange", doc, obj);
6557 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6558
6559 if (obj.canceled) { return null }
6560 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6561 }
6562
6563 // Apply a change to a document, and add it to the document's
6564 // history, and propagating it to all linked documents.
6565 function makeChange(doc, change, ignoreReadOnly) {
6566 if (doc.cm) {
6567 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6568 if (doc.cm.state.suppressEdits) { return }
6569 }
6570
6571 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6572 change = filterChange(doc, change, true);
6573 if (!change) { return }
6574 }
6575
6576 // Possibly split or suppress the update based on the presence
6577 // of read-only spans in its range.
6578 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6579 if (split) {
6580 for (var i = split.length - 1; i >= 0; --i)
6581 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6582 } else {
6583 makeChangeInner(doc, change);
6584 }
6585 }
6586
6587 function makeChangeInner(doc, change) {
6588 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6589 var selAfter = computeSelAfterChange(doc, change);
6590 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6591
6592 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6593 var rebased = [];
6594
6595 linkedDocs(doc, function (doc, sharedHist) {
6596 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6597 rebaseHist(doc.history, change);
6598 rebased.push(doc.history);
6599 }
6600 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6601 });
6602 }
6603
6604 // Revert a change stored in a document's history.
6605 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6606 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
6607
6608 var hist = doc.history, event, selAfter = doc.sel;
6609 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6610
6611 // Verify that there is a useable event (so that ctrl-z won't
6612 // needlessly clear selection events)
6613 var i = 0;
6614 for (; i < source.length; i++) {
6615 event = source[i];
6616 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6617 { break }
6618 }
6619 if (i == source.length) { return }
6620 hist.lastOrigin = hist.lastSelOrigin = null;
6621
6622 for (;;) {
6623 event = source.pop();
6624 if (event.ranges) {
6625 pushSelectionToHistory(event, dest);
6626 if (allowSelectionOnly && !event.equals(doc.sel)) {
6627 setSelection(doc, event, {clearRedo: false});
6628 return
6629 }
6630 selAfter = event;
6631 }
6632 else { break }
6633 }
6634
6635 // Build up a reverse change object to add to the opposite history
6636 // stack (redo when undoing, and vice versa).
6637 var antiChanges = [];
6638 pushSelectionToHistory(selAfter, dest);
6639 dest.push({changes: antiChanges, generation: hist.generation});
6640 hist.generation = event.generation || ++hist.maxGeneration;
6641
6642 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6643
6644 var loop = function ( i ) {
6645 var change = event.changes[i];
6646 change.origin = type;
6647 if (filter && !filterChange(doc, change, false)) {
6648 source.length = 0;
6649 return {}
6650 }
6651
6652 antiChanges.push(historyChangeFromChange(doc, change));
6653
6654 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6655 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6656 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6657 var rebased = [];
6658
6659 // Propagate to the linked documents
6660 linkedDocs(doc, function (doc, sharedHist) {
6661 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6662 rebaseHist(doc.history, change);
6663 rebased.push(doc.history);
6664 }
6665 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6666 });
6667 };
6668
6669 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6670 var returned = loop( i$1 );
6671
6672 if ( returned ) return returned.v;
6673 }
6674 }
6675
6676 // Sub-views need their line numbers shifted when text is added
6677 // above or below them in the parent document.
6678 function shiftDoc(doc, distance) {
6679 if (distance == 0) { return }
6680 doc.first += distance;
6681 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6682 Pos(range.anchor.line + distance, range.anchor.ch),
6683 Pos(range.head.line + distance, range.head.ch)
6684 ); }), doc.sel.primIndex);
6685 if (doc.cm) {
6686 regChange(doc.cm, doc.first, doc.first - distance, distance);
6687 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6688 { regLineChange(doc.cm, l, "gutter"); }
6689 }
6690 }
6691
6692 // More lower-level change function, handling only a single document
6693 // (not linked ones).
6694 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6695 if (doc.cm && !doc.cm.curOp)
6696 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6697
6698 if (change.to.line < doc.first) {
6699 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6700 return
6701 }
6702 if (change.from.line > doc.lastLine()) { return }
6703
6704 // Clip the change to the size of this doc
6705 if (change.from.line < doc.first) {
6706 var shift = change.text.length - 1 - (doc.first - change.from.line);
6707 shiftDoc(doc, shift);
6708 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6709 text: [lst(change.text)], origin: change.origin};
6710 }
6711 var last = doc.lastLine();
6712 if (change.to.line > last) {
6713 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6714 text: [change.text[0]], origin: change.origin};
6715 }
6716
6717 change.removed = getBetween(doc, change.from, change.to);
6718
6719 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6720 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6721 else { updateDoc(doc, change, spans); }
6722 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6723 }
6724
6725 // Handle the interaction of a change to a document with the editor
6726 // that this document is part of.
6727 function makeChangeSingleDocInEditor(cm, change, spans) {
6728 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6729
6730 var recomputeMaxLength = false, checkWidthStart = from.line;
6731 if (!cm.options.lineWrapping) {
6732 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6733 doc.iter(checkWidthStart, to.line + 1, function (line) {
6734 if (line == display.maxLine) {
6735 recomputeMaxLength = true;
6736 return true
6737 }
6738 });
6739 }
6740
6741 if (doc.sel.contains(change.from, change.to) > -1)
6742 { signalCursorActivity(cm); }
6743
6744 updateDoc(doc, change, spans, estimateHeight(cm));
6745
6746 if (!cm.options.lineWrapping) {
6747 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
6748 var len = lineLength(line);
6749 if (len > display.maxLineLength) {
6750 display.maxLine = line;
6751 display.maxLineLength = len;
6752 display.maxLineChanged = true;
6753 recomputeMaxLength = false;
6754 }
6755 });
6756 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
6757 }
6758
6759 retreatFrontier(doc, from.line);
6760 startWorker(cm, 400);
6761
6762 var lendiff = change.text.length - (to.line - from.line) - 1;
6763 // Remember that these lines changed, for updating the display
6764 if (change.full)
6765 { regChange(cm); }
6766 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
6767 { regLineChange(cm, from.line, "text"); }
6768 else
6769 { regChange(cm, from.line, to.line + 1, lendiff); }
6770
6771 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
6772 if (changeHandler || changesHandler) {
6773 var obj = {
6774 from: from, to: to,
6775 text: change.text,
6776 removed: change.removed,
6777 origin: change.origin
6778 };
6779 if (changeHandler) { signalLater(cm, "change", cm, obj); }
6780 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
6781 }
6782 cm.display.selForContextMenu = null;
6783 }
6784
6785 function replaceRange(doc, code, from, to, origin) {
6786 if (!to) { to = from; }
6787 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
6788 if (typeof code == "string") { code = doc.splitLines(code); }
6789 makeChange(doc, {from: from, to: to, text: code, origin: origin});
6790 }
6791
6792 // Rebasing/resetting history to deal with externally-sourced changes
6793
6794 function rebaseHistSelSingle(pos, from, to, diff) {
6795 if (to < pos.line) {
6796 pos.line += diff;
6797 } else if (from < pos.line) {
6798 pos.line = from;
6799 pos.ch = 0;
6800 }
6801 }
6802
6803 // Tries to rebase an array of history events given a change in the
6804 // document. If the change touches the same lines as the event, the
6805 // event, and everything 'behind' it, is discarded. If the change is
6806 // before the event, the event's positions are updated. Uses a
6807 // copy-on-write scheme for the positions, to avoid having to
6808 // reallocate them all on every rebase, but also avoid problems with
6809 // shared position objects being unsafely updated.
6810 function rebaseHistArray(array, from, to, diff) {
6811 for (var i = 0; i < array.length; ++i) {
6812 var sub = array[i], ok = true;
6813 if (sub.ranges) {
6814 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
6815 for (var j = 0; j < sub.ranges.length; j++) {
6816 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
6817 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
6818 }
6819 continue
6820 }
6821 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
6822 var cur = sub.changes[j$1];
6823 if (to < cur.from.line) {
6824 cur.from = Pos(cur.from.line + diff, cur.from.ch);
6825 cur.to = Pos(cur.to.line + diff, cur.to.ch);
6826 } else if (from <= cur.to.line) {
6827 ok = false;
6828 break
6829 }
6830 }
6831 if (!ok) {
6832 array.splice(0, i + 1);
6833 i = 0;
6834 }
6835 }
6836 }
6837
6838 function rebaseHist(hist, change) {
6839 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
6840 rebaseHistArray(hist.done, from, to, diff);
6841 rebaseHistArray(hist.undone, from, to, diff);
6842 }
6843
6844 // Utility for applying a change to a line by handle or number,
6845 // returning the number and optionally registering the line as
6846 // changed.
6847 function changeLine(doc, handle, changeType, op) {
6848 var no = handle, line = handle;
6849 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
6850 else { no = lineNo(handle); }
6851 if (no == null) { return null }
6852 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
6853 return line
6854 }
6855
6856 // The document is represented as a BTree consisting of leaves, with
6857 // chunk of lines in them, and branches, with up to ten leaves or
6858 // other branch nodes below them. The top node is always a branch
6859 // node, and is the document object itself (meaning it has
6860 // additional methods and properties).
6861 //
6862 // All nodes have parent links. The tree is used both to go from
6863 // line numbers to line objects, and to go from objects to numbers.
6864 // It also indexes by height, and is used to convert between height
6865 // and line object, and to find the total height of the document.
6866 //
6867 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
6868
6869 function LeafChunk(lines) {
6870 var this$1 = this;
6871
6872 this.lines = lines;
6873 this.parent = null;
6874 var height = 0;
6875 for (var i = 0; i < lines.length; ++i) {
6876 lines[i].parent = this$1;
6877 height += lines[i].height;
6878 }
6879 this.height = height;
6880 }
6881
6882 LeafChunk.prototype = {
6883 chunkSize: function chunkSize() { return this.lines.length },
6884
6885 // Remove the n lines at offset 'at'.
6886 removeInner: function removeInner(at, n) {
6887 var this$1 = this;
6888
6889 for (var i = at, e = at + n; i < e; ++i) {
6890 var line = this$1.lines[i];
6891 this$1.height -= line.height;
6892 cleanUpLine(line);
6893 signalLater(line, "delete");
6894 }
6895 this.lines.splice(at, n);
6896 },
6897
6898 // Helper used to collapse a small branch into a single leaf.
6899 collapse: function collapse(lines) {
6900 lines.push.apply(lines, this.lines);
6901 },
6902
6903 // Insert the given array of lines at offset 'at', count them as
6904 // having the given height.
6905 insertInner: function insertInner(at, lines, height) {
6906 var this$1 = this;
6907
6908 this.height += height;
6909 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
6910 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
6911 },
6912
6913 // Used to iterate over a part of the tree.
6914 iterN: function iterN(at, n, op) {
6915 var this$1 = this;
6916
6917 for (var e = at + n; at < e; ++at)
6918 { if (op(this$1.lines[at])) { return true } }
6919 }
6920 };
6921
6922 function BranchChunk(children) {
6923 var this$1 = this;
6924
6925 this.children = children;
6926 var size = 0, height = 0;
6927 for (var i = 0; i < children.length; ++i) {
6928 var ch = children[i];
6929 size += ch.chunkSize(); height += ch.height;
6930 ch.parent = this$1;
6931 }
6932 this.size = size;
6933 this.height = height;
6934 this.parent = null;
6935 }
6936
6937 BranchChunk.prototype = {
6938 chunkSize: function chunkSize() { return this.size },
6939
6940 removeInner: function removeInner(at, n) {
6941 var this$1 = this;
6942
6943 this.size -= n;
6944 for (var i = 0; i < this.children.length; ++i) {
6945 var child = this$1.children[i], sz = child.chunkSize();
6946 if (at < sz) {
6947 var rm = Math.min(n, sz - at), oldHeight = child.height;
6948 child.removeInner(at, rm);
6949 this$1.height -= oldHeight - child.height;
6950 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
6951 if ((n -= rm) == 0) { break }
6952 at = 0;
6953 } else { at -= sz; }
6954 }
6955 // If the result is smaller than 25 lines, ensure that it is a
6956 // single leaf node.
6957 if (this.size - n < 25 &&
6958 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
6959 var lines = [];
6960 this.collapse(lines);
6961 this.children = [new LeafChunk(lines)];
6962 this.children[0].parent = this;
6963 }
6964 },
6965
6966 collapse: function collapse(lines) {
6967 var this$1 = this;
6968
6969 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
6970 },
6971
6972 insertInner: function insertInner(at, lines, height) {
6973 var this$1 = this;
6974
6975 this.size += lines.length;
6976 this.height += height;
6977 for (var i = 0; i < this.children.length; ++i) {
6978 var child = this$1.children[i], sz = child.chunkSize();
6979 if (at <= sz) {
6980 child.insertInner(at, lines, height);
6981 if (child.lines && child.lines.length > 50) {
6982 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
6983 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
6984 var remaining = child.lines.length % 25 + 25;
6985 for (var pos = remaining; pos < child.lines.length;) {
6986 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
6987 child.height -= leaf.height;
6988 this$1.children.splice(++i, 0, leaf);
6989 leaf.parent = this$1;
6990 }
6991 child.lines = child.lines.slice(0, remaining);
6992 this$1.maybeSpill();
6993 }
6994 break
6995 }
6996 at -= sz;
6997 }
6998 },
6999
7000 // When a node has grown, check whether it should be split.
7001 maybeSpill: function maybeSpill() {
7002 if (this.children.length <= 10) { return }
7003 var me = this;
7004 do {
7005 var spilled = me.children.splice(me.children.length - 5, 5);
7006 var sibling = new BranchChunk(spilled);
7007 if (!me.parent) { // Become the parent node
7008 var copy = new BranchChunk(me.children);
7009 copy.parent = me;
7010 me.children = [copy, sibling];
7011 me = copy;
7012 } else {
7013 me.size -= sibling.size;
7014 me.height -= sibling.height;
7015 var myIndex = indexOf(me.parent.children, me);
7016 me.parent.children.splice(myIndex + 1, 0, sibling);
7017 }
7018 sibling.parent = me.parent;
7019 } while (me.children.length > 10)
7020 me.parent.maybeSpill();
7021 },
7022
7023 iterN: function iterN(at, n, op) {
7024 var this$1 = this;
7025
7026 for (var i = 0; i < this.children.length; ++i) {
7027 var child = this$1.children[i], sz = child.chunkSize();
7028 if (at < sz) {
7029 var used = Math.min(n, sz - at);
7030 if (child.iterN(at, used, op)) { return true }
7031 if ((n -= used) == 0) { break }
7032 at = 0;
7033 } else { at -= sz; }
7034 }
7035 }
7036 };
7037
7038 // Line widgets are block elements displayed above or below a line.
7039
7040 var LineWidget = function(doc, node, options) {
7041 var this$1 = this;
7042
7043 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7044 { this$1[opt] = options[opt]; } } }
7045 this.doc = doc;
7046 this.node = node;
7047 };
7048
7049 LineWidget.prototype.clear = function () {
7050 var this$1 = this;
7051
7052 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7053 if (no == null || !ws) { return }
7054 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7055 if (!ws.length) { line.widgets = null; }
7056 var height = widgetHeight(this);
7057 updateLineHeight(line, Math.max(0, line.height - height));
7058 if (cm) {
7059 runInOp(cm, function () {
7060 adjustScrollWhenAboveVisible(cm, line, -height);
7061 regLineChange(cm, no, "widget");
7062 });
7063 signalLater(cm, "lineWidgetCleared", cm, this, no);
7064 }
7065 };
7066
7067 LineWidget.prototype.changed = function () {
7068 var this$1 = this;
7069
7070 var oldH = this.height, cm = this.doc.cm, line = this.line;
7071 this.height = null;
7072 var diff = widgetHeight(this) - oldH;
7073 if (!diff) { return }
7074 updateLineHeight(line, line.height + diff);
7075 if (cm) {
7076 runInOp(cm, function () {
7077 cm.curOp.forceUpdate = true;
7078 adjustScrollWhenAboveVisible(cm, line, diff);
7079 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7080 });
7081 }
7082 };
7083 eventMixin(LineWidget);
7084
7085 function adjustScrollWhenAboveVisible(cm, line, diff) {
7086 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7087 { addToScrollTop(cm, diff); }
7088 }
7089
7090 function addLineWidget(doc, handle, node, options) {
7091 var widget = new LineWidget(doc, node, options);
7092 var cm = doc.cm;
7093 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7094 changeLine(doc, handle, "widget", function (line) {
7095 var widgets = line.widgets || (line.widgets = []);
7096 if (widget.insertAt == null) { widgets.push(widget); }
7097 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7098 widget.line = line;
7099 if (cm && !lineIsHidden(doc, line)) {
7100 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7101 updateLineHeight(line, line.height + widgetHeight(widget));
7102 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7103 cm.curOp.forceUpdate = true;
7104 }
7105 return true
7106 });
7107 signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle));
7108 return widget
7109 }
7110
7111 // TEXTMARKERS
7112
7113 // Created with markText and setBookmark methods. A TextMarker is a
7114 // handle that can be used to clear or find a marked position in the
7115 // document. Line objects hold arrays (markedSpans) containing
7116 // {from, to, marker} object pointing to such marker objects, and
7117 // indicating that such a marker is present on that line. Multiple
7118 // lines may point to the same marker when it spans across lines.
7119 // The spans will have null for their from/to properties when the
7120 // marker continues beyond the start/end of the line. Markers have
7121 // links back to the lines they currently touch.
7122
7123 // Collapsed markers have unique ids, in order to be able to order
7124 // them, which is needed for uniquely determining an outer marker
7125 // when they overlap (they may nest, but not partially overlap).
7126 var nextMarkerId = 0;
7127
7128 var TextMarker = function(doc, type) {
7129 this.lines = [];
7130 this.type = type;
7131 this.doc = doc;
7132 this.id = ++nextMarkerId;
7133 };
7134
7135 // Clear the marker.
7136 TextMarker.prototype.clear = function () {
7137 var this$1 = this;
7138
7139 if (this.explicitlyCleared) { return }
7140 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7141 if (withOp) { startOperation(cm); }
7142 if (hasHandler(this, "clear")) {
7143 var found = this.find();
7144 if (found) { signalLater(this, "clear", found.from, found.to); }
7145 }
7146 var min = null, max = null;
7147 for (var i = 0; i < this.lines.length; ++i) {
7148 var line = this$1.lines[i];
7149 var span = getMarkedSpanFor(line.markedSpans, this$1);
7150 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7151 else if (cm) {
7152 if (span.to != null) { max = lineNo(line); }
7153 if (span.from != null) { min = lineNo(line); }
7154 }
7155 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7156 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7157 { updateLineHeight(line, textHeight(cm.display)); }
7158 }
7159 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7160 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7161 if (len > cm.display.maxLineLength) {
7162 cm.display.maxLine = visual;
7163 cm.display.maxLineLength = len;
7164 cm.display.maxLineChanged = true;
7165 }
7166 } }
7167
7168 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7169 this.lines.length = 0;
7170 this.explicitlyCleared = true;
7171 if (this.atomic && this.doc.cantEdit) {
7172 this.doc.cantEdit = false;
7173 if (cm) { reCheckSelection(cm.doc); }
7174 }
7175 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7176 if (withOp) { endOperation(cm); }
7177 if (this.parent) { this.parent.clear(); }
7178 };
7179
7180 // Find the position of the marker in the document. Returns a {from,
7181 // to} object by default. Side can be passed to get a specific side
7182 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7183 // Pos objects returned contain a line object, rather than a line
7184 // number (used to prevent looking up the same line twice).
7185 TextMarker.prototype.find = function (side, lineObj) {
7186 var this$1 = this;
7187
7188 if (side == null && this.type == "bookmark") { side = 1; }
7189 var from, to;
7190 for (var i = 0; i < this.lines.length; ++i) {
7191 var line = this$1.lines[i];
7192 var span = getMarkedSpanFor(line.markedSpans, this$1);
7193 if (span.from != null) {
7194 from = Pos(lineObj ? line : lineNo(line), span.from);
7195 if (side == -1) { return from }
7196 }
7197 if (span.to != null) {
7198 to = Pos(lineObj ? line : lineNo(line), span.to);
7199 if (side == 1) { return to }
7200 }
7201 }
7202 return from && {from: from, to: to}
7203 };
7204
7205 // Signals that the marker's widget changed, and surrounding layout
7206 // should be recomputed.
7207 TextMarker.prototype.changed = function () {
7208 var this$1 = this;
7209
7210 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7211 if (!pos || !cm) { return }
7212 runInOp(cm, function () {
7213 var line = pos.line, lineN = lineNo(pos.line);
7214 var view = findViewForLine(cm, lineN);
7215 if (view) {
7216 clearLineMeasurementCacheFor(view);
7217 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7218 }
7219 cm.curOp.updateMaxLine = true;
7220 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7221 var oldHeight = widget.height;
7222 widget.height = null;
7223 var dHeight = widgetHeight(widget) - oldHeight;
7224 if (dHeight)
7225 { updateLineHeight(line, line.height + dHeight); }
7226 }
7227 signalLater(cm, "markerChanged", cm, this$1);
7228 });
7229 };
7230
7231 TextMarker.prototype.attachLine = function (line) {
7232 if (!this.lines.length && this.doc.cm) {
7233 var op = this.doc.cm.curOp;
7234 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7235 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7236 }
7237 this.lines.push(line);
7238 };
7239
7240 TextMarker.prototype.detachLine = function (line) {
7241 this.lines.splice(indexOf(this.lines, line), 1);
7242 if (!this.lines.length && this.doc.cm) {
7243 var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7244 }
7245 };
7246 eventMixin(TextMarker);
7247
7248 // Create a marker, wire it up to the right lines, and
7249 function markText(doc, from, to, options, type) {
7250 // Shared markers (across linked documents) are handled separately
7251 // (markTextShared will call out to this again, once per
7252 // document).
7253 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7254 // Ensure we are in an operation.
7255 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7256
7257 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7258 if (options) { copyObj(options, marker, false); }
7259 // Don't connect empty markers unless clearWhenEmpty is false
7260 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7261 { return marker }
7262 if (marker.replacedWith) {
7263 // Showing up as a widget implies collapsed (widget replaces text)
7264 marker.collapsed = true;
7265 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7266 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7267 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7268 }
7269 if (marker.collapsed) {
7270 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7271 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7272 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7273 seeCollapsedSpans();
7274 }
7275
7276 if (marker.addToHistory)
7277 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7278
7279 var curLine = from.line, cm = doc.cm, updateMaxLine;
7280 doc.iter(curLine, to.line + 1, function (line) {
7281 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7282 { updateMaxLine = true; }
7283 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7284 addMarkedSpan(line, new MarkedSpan(marker,
7285 curLine == from.line ? from.ch : null,
7286 curLine == to.line ? to.ch : null));
7287 ++curLine;
7288 });
7289 // lineIsHidden depends on the presence of the spans, so needs a second pass
7290 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7291 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7292 }); }
7293
7294 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7295
7296 if (marker.readOnly) {
7297 seeReadOnlySpans();
7298 if (doc.history.done.length || doc.history.undone.length)
7299 { doc.clearHistory(); }
7300 }
7301 if (marker.collapsed) {
7302 marker.id = ++nextMarkerId;
7303 marker.atomic = true;
7304 }
7305 if (cm) {
7306 // Sync editor state
7307 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7308 if (marker.collapsed)
7309 { regChange(cm, from.line, to.line + 1); }
7310 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
7311 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7312 if (marker.atomic) { reCheckSelection(cm.doc); }
7313 signalLater(cm, "markerAdded", cm, marker);
7314 }
7315 return marker
7316 }
7317
7318 // SHARED TEXTMARKERS
7319
7320 // A shared marker spans multiple linked documents. It is
7321 // implemented as a meta-marker-object controlling multiple normal
7322 // markers.
7323 var SharedTextMarker = function(markers, primary) {
7324 var this$1 = this;
7325
7326 this.markers = markers;
7327 this.primary = primary;
7328 for (var i = 0; i < markers.length; ++i)
7329 { markers[i].parent = this$1; }
7330 };
7331
7332 SharedTextMarker.prototype.clear = function () {
7333 var this$1 = this;
7334
7335 if (this.explicitlyCleared) { return }
7336 this.explicitlyCleared = true;
7337 for (var i = 0; i < this.markers.length; ++i)
7338 { this$1.markers[i].clear(); }
7339 signalLater(this, "clear");
7340 };
7341
7342 SharedTextMarker.prototype.find = function (side, lineObj) {
7343 return this.primary.find(side, lineObj)
7344 };
7345 eventMixin(SharedTextMarker);
7346
7347 function markTextShared(doc, from, to, options, type) {
7348 options = copyObj(options);
7349 options.shared = false;
7350 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7351 var widget = options.widgetNode;
7352 linkedDocs(doc, function (doc) {
7353 if (widget) { options.widgetNode = widget.cloneNode(true); }
7354 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7355 for (var i = 0; i < doc.linked.length; ++i)
7356 { if (doc.linked[i].isParent) { return } }
7357 primary = lst(markers);
7358 });
7359 return new SharedTextMarker(markers, primary)
7360 }
7361
7362 function findSharedMarkers(doc) {
7363 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7364 }
7365
7366 function copySharedMarkers(doc, markers) {
7367 for (var i = 0; i < markers.length; i++) {
7368 var marker = markers[i], pos = marker.find();
7369 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7370 if (cmp(mFrom, mTo)) {
7371 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7372 marker.markers.push(subMark);
7373 subMark.parent = marker;
7374 }
7375 }
7376 }
7377
7378 function detachSharedMarkers(markers) {
7379 var loop = function ( i ) {
7380 var marker = markers[i], linked = [marker.primary.doc];
7381 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7382 for (var j = 0; j < marker.markers.length; j++) {
7383 var subMarker = marker.markers[j];
7384 if (indexOf(linked, subMarker.doc) == -1) {
7385 subMarker.parent = null;
7386 marker.markers.splice(j--, 1);
7387 }
7388 }
7389 };
7390
7391 for (var i = 0; i < markers.length; i++) loop( i );
7392 }
7393
7394 var nextDocId = 0;
7395 var Doc = function(text, mode, firstLine, lineSep, direction) {
7396 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7397 if (firstLine == null) { firstLine = 0; }
7398
7399 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7400 this.first = firstLine;
7401 this.scrollTop = this.scrollLeft = 0;
7402 this.cantEdit = false;
7403 this.cleanGeneration = 1;
7404 this.modeFrontier = this.highlightFrontier = firstLine;
7405 var start = Pos(firstLine, 0);
7406 this.sel = simpleSelection(start);
7407 this.history = new History(null);
7408 this.id = ++nextDocId;
7409 this.modeOption = mode;
7410 this.lineSep = lineSep;
7411 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7412 this.extend = false;
7413
7414 if (typeof text == "string") { text = this.splitLines(text); }
7415 updateDoc(this, {from: start, to: start, text: text});
7416 setSelection(this, simpleSelection(start), sel_dontScroll);
7417 };
7418
7419 Doc.prototype = createObj(BranchChunk.prototype, {
7420 constructor: Doc,
7421 // Iterate over the document. Supports two forms -- with only one
7422 // argument, it calls that for each line in the document. With
7423 // three, it iterates over the range given by the first two (with
7424 // the second being non-inclusive).
7425 iter: function(from, to, op) {
7426 if (op) { this.iterN(from - this.first, to - from, op); }
7427 else { this.iterN(this.first, this.first + this.size, from); }
7428 },
7429
7430 // Non-public interface for adding and removing lines.
7431 insert: function(at, lines) {
7432 var height = 0;
7433 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7434 this.insertInner(at - this.first, lines, height);
7435 },
7436 remove: function(at, n) { this.removeInner(at - this.first, n); },
7437
7438 // From here, the methods are part of the public interface. Most
7439 // are also available from CodeMirror (editor) instances.
7440
7441 getValue: function(lineSep) {
7442 var lines = getLines(this, this.first, this.first + this.size);
7443 if (lineSep === false) { return lines }
7444 return lines.join(lineSep || this.lineSeparator())
7445 },
7446 setValue: docMethodOp(function(code) {
7447 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7448 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7449 text: this.splitLines(code), origin: "setValue", full: true}, true);
7450 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7451 setSelection(this, simpleSelection(top), sel_dontScroll);
7452 }),
7453 replaceRange: function(code, from, to, origin) {
7454 from = clipPos(this, from);
7455 to = to ? clipPos(this, to) : from;
7456 replaceRange(this, code, from, to, origin);
7457 },
7458 getRange: function(from, to, lineSep) {
7459 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7460 if (lineSep === false) { return lines }
7461 return lines.join(lineSep || this.lineSeparator())
7462 },
7463
7464 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7465
7466 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7467 getLineNumber: function(line) {return lineNo(line)},
7468
7469 getLineHandleVisualStart: function(line) {
7470 if (typeof line == "number") { line = getLine(this, line); }
7471 return visualLine(line)
7472 },
7473
7474 lineCount: function() {return this.size},
7475 firstLine: function() {return this.first},
7476 lastLine: function() {return this.first + this.size - 1},
7477
7478 clipPos: function(pos) {return clipPos(this, pos)},
7479
7480 getCursor: function(start) {
7481 var range$$1 = this.sel.primary(), pos;
7482 if (start == null || start == "head") { pos = range$$1.head; }
7483 else if (start == "anchor") { pos = range$$1.anchor; }
7484 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7485 else { pos = range$$1.from(); }
7486 return pos
7487 },
7488 listSelections: function() { return this.sel.ranges },
7489 somethingSelected: function() {return this.sel.somethingSelected()},
7490
7491 setCursor: docMethodOp(function(line, ch, options) {
7492 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7493 }),
7494 setSelection: docMethodOp(function(anchor, head, options) {
7495 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7496 }),
7497 extendSelection: docMethodOp(function(head, other, options) {
7498 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7499 }),
7500 extendSelections: docMethodOp(function(heads, options) {
7501 extendSelections(this, clipPosArray(this, heads), options);
7502 }),
7503 extendSelectionsBy: docMethodOp(function(f, options) {
7504 var heads = map(this.sel.ranges, f);
7505 extendSelections(this, clipPosArray(this, heads), options);
7506 }),
7507 setSelections: docMethodOp(function(ranges, primary, options) {
7508 var this$1 = this;
7509
7510 if (!ranges.length) { return }
7511 var out = [];
7512 for (var i = 0; i < ranges.length; i++)
7513 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7514 clipPos(this$1, ranges[i].head)); }
7515 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7516 setSelection(this, normalizeSelection(out, primary), options);
7517 }),
7518 addSelection: docMethodOp(function(anchor, head, options) {
7519 var ranges = this.sel.ranges.slice(0);
7520 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7521 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7522 }),
7523
7524 getSelection: function(lineSep) {
7525 var this$1 = this;
7526
7527 var ranges = this.sel.ranges, lines;
7528 for (var i = 0; i < ranges.length; i++) {
7529 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7530 lines = lines ? lines.concat(sel) : sel;
7531 }
7532 if (lineSep === false) { return lines }
7533 else { return lines.join(lineSep || this.lineSeparator()) }
7534 },
7535 getSelections: function(lineSep) {
7536 var this$1 = this;
7537
7538 var parts = [], ranges = this.sel.ranges;
7539 for (var i = 0; i < ranges.length; i++) {
7540 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7541 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7542 parts[i] = sel;
7543 }
7544 return parts
7545 },
7546 replaceSelection: function(code, collapse, origin) {
7547 var dup = [];
7548 for (var i = 0; i < this.sel.ranges.length; i++)
7549 { dup[i] = code; }
7550 this.replaceSelections(dup, collapse, origin || "+input");
7551 },
7552 replaceSelections: docMethodOp(function(code, collapse, origin) {
7553 var this$1 = this;
7554
7555 var changes = [], sel = this.sel;
7556 for (var i = 0; i < sel.ranges.length; i++) {
7557 var range$$1 = sel.ranges[i];
7558 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7559 }
7560 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7561 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7562 { makeChange(this$1, changes[i$1]); }
7563 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7564 else if (this.cm) { ensureCursorVisible(this.cm); }
7565 }),
7566 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7567 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7568 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7569 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7570
7571 setExtending: function(val) {this.extend = val;},
7572 getExtending: function() {return this.extend},
7573
7574 historySize: function() {
7575 var hist = this.history, done = 0, undone = 0;
7576 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7577 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7578 return {undo: done, redo: undone}
7579 },
7580 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7581
7582 markClean: function() {
7583 this.cleanGeneration = this.changeGeneration(true);
7584 },
7585 changeGeneration: function(forceSplit) {
7586 if (forceSplit)
7587 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7588 return this.history.generation
7589 },
7590 isClean: function (gen) {
7591 return this.history.generation == (gen || this.cleanGeneration)
7592 },
7593
7594 getHistory: function() {
7595 return {done: copyHistoryArray(this.history.done),
7596 undone: copyHistoryArray(this.history.undone)}
7597 },
7598 setHistory: function(histData) {
7599 var hist = this.history = new History(this.history.maxGeneration);
7600 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7601 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7602 },
7603
7604 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7605 return changeLine(this, line, "gutter", function (line) {
7606 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7607 markers[gutterID] = value;
7608 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7609 return true
7610 })
7611 }),
7612
7613 clearGutter: docMethodOp(function(gutterID) {
7614 var this$1 = this;
7615
7616 this.iter(function (line) {
7617 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7618 changeLine(this$1, line, "gutter", function () {
7619 line.gutterMarkers[gutterID] = null;
7620 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7621 return true
7622 });
7623 }
7624 });
7625 }),
7626
7627 lineInfo: function(line) {
7628 var n;
7629 if (typeof line == "number") {
7630 if (!isLine(this, line)) { return null }
7631 n = line;
7632 line = getLine(this, line);
7633 if (!line) { return null }
7634 } else {
7635 n = lineNo(line);
7636 if (n == null) { return null }
7637 }
7638 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7639 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7640 widgets: line.widgets}
7641 },
7642
7643 addLineClass: docMethodOp(function(handle, where, cls) {
7644 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7645 var prop = where == "text" ? "textClass"
7646 : where == "background" ? "bgClass"
7647 : where == "gutter" ? "gutterClass" : "wrapClass";
7648 if (!line[prop]) { line[prop] = cls; }
7649 else if (classTest(cls).test(line[prop])) { return false }
7650 else { line[prop] += " " + cls; }
7651 return true
7652 })
7653 }),
7654 removeLineClass: docMethodOp(function(handle, where, cls) {
7655 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7656 var prop = where == "text" ? "textClass"
7657 : where == "background" ? "bgClass"
7658 : where == "gutter" ? "gutterClass" : "wrapClass";
7659 var cur = line[prop];
7660 if (!cur) { return false }
7661 else if (cls == null) { line[prop] = null; }
7662 else {
7663 var found = cur.match(classTest(cls));
7664 if (!found) { return false }
7665 var end = found.index + found[0].length;
7666 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7667 }
7668 return true
7669 })
7670 }),
7671
7672 addLineWidget: docMethodOp(function(handle, node, options) {
7673 return addLineWidget(this, handle, node, options)
7674 }),
7675 removeLineWidget: function(widget) { widget.clear(); },
7676
7677 markText: function(from, to, options) {
7678 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7679 },
7680 setBookmark: function(pos, options) {
7681 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7682 insertLeft: options && options.insertLeft,
7683 clearWhenEmpty: false, shared: options && options.shared,
7684 handleMouseEvents: options && options.handleMouseEvents};
7685 pos = clipPos(this, pos);
7686 return markText(this, pos, pos, realOpts, "bookmark")
7687 },
7688 findMarksAt: function(pos) {
7689 pos = clipPos(this, pos);
7690 var markers = [], spans = getLine(this, pos.line).markedSpans;
7691 if (spans) { for (var i = 0; i < spans.length; ++i) {
7692 var span = spans[i];
7693 if ((span.from == null || span.from <= pos.ch) &&
7694 (span.to == null || span.to >= pos.ch))
7695 { markers.push(span.marker.parent || span.marker); }
7696 } }
7697 return markers
7698 },
7699 findMarks: function(from, to, filter) {
7700 from = clipPos(this, from); to = clipPos(this, to);
7701 var found = [], lineNo$$1 = from.line;
7702 this.iter(from.line, to.line + 1, function (line) {
7703 var spans = line.markedSpans;
7704 if (spans) { for (var i = 0; i < spans.length; i++) {
7705 var span = spans[i];
7706 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7707 span.from == null && lineNo$$1 != from.line ||
7708 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7709 (!filter || filter(span.marker)))
7710 { found.push(span.marker.parent || span.marker); }
7711 } }
7712 ++lineNo$$1;
7713 });
7714 return found
7715 },
7716 getAllMarks: function() {
7717 var markers = [];
7718 this.iter(function (line) {
7719 var sps = line.markedSpans;
7720 if (sps) { for (var i = 0; i < sps.length; ++i)
7721 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7722 });
7723 return markers
7724 },
7725
7726 posFromIndex: function(off) {
7727 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7728 this.iter(function (line) {
7729 var sz = line.text.length + sepSize;
7730 if (sz > off) { ch = off; return true }
7731 off -= sz;
7732 ++lineNo$$1;
7733 });
7734 return clipPos(this, Pos(lineNo$$1, ch))
7735 },
7736 indexFromPos: function (coords) {
7737 coords = clipPos(this, coords);
7738 var index = coords.ch;
7739 if (coords.line < this.first || coords.ch < 0) { return 0 }
7740 var sepSize = this.lineSeparator().length;
7741 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
7742 index += line.text.length + sepSize;
7743 });
7744 return index
7745 },
7746
7747 copy: function(copyHistory) {
7748 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7749 this.modeOption, this.first, this.lineSep, this.direction);
7750 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7751 doc.sel = this.sel;
7752 doc.extend = false;
7753 if (copyHistory) {
7754 doc.history.undoDepth = this.history.undoDepth;
7755 doc.setHistory(this.getHistory());
7756 }
7757 return doc
7758 },
7759
7760 linkedDoc: function(options) {
7761 if (!options) { options = {}; }
7762 var from = this.first, to = this.first + this.size;
7763 if (options.from != null && options.from > from) { from = options.from; }
7764 if (options.to != null && options.to < to) { to = options.to; }
7765 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
7766 if (options.sharedHist) { copy.history = this.history
7767 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7768 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7769 copySharedMarkers(copy, findSharedMarkers(this));
7770 return copy
7771 },
7772 unlinkDoc: function(other) {
7773 var this$1 = this;
7774
7775 if (other instanceof CodeMirror$1) { other = other.doc; }
7776 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
7777 var link = this$1.linked[i];
7778 if (link.doc != other) { continue }
7779 this$1.linked.splice(i, 1);
7780 other.unlinkDoc(this$1);
7781 detachSharedMarkers(findSharedMarkers(this$1));
7782 break
7783 } }
7784 // If the histories were shared, split them again
7785 if (other.history == this.history) {
7786 var splitIds = [other.id];
7787 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
7788 other.history = new History(null);
7789 other.history.done = copyHistoryArray(this.history.done, splitIds);
7790 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7791 }
7792 },
7793 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7794
7795 getMode: function() {return this.mode},
7796 getEditor: function() {return this.cm},
7797
7798 splitLines: function(str) {
7799 if (this.lineSep) { return str.split(this.lineSep) }
7800 return splitLinesAuto(str)
7801 },
7802 lineSeparator: function() { return this.lineSep || "\n" },
7803
7804 setDirection: docMethodOp(function (dir) {
7805 if (dir != "rtl") { dir = "ltr"; }
7806 if (dir == this.direction) { return }
7807 this.direction = dir;
7808 this.iter(function (line) { return line.order = null; });
7809 if (this.cm) { directionChanged(this.cm); }
7810 })
7811 });
7812
7813 // Public alias.
7814 Doc.prototype.eachLine = Doc.prototype.iter;
7815
7816 // Kludge to work around strange IE behavior where it'll sometimes
7817 // re-fire a series of drag-related events right after the drop (#1551)
7818 var lastDrop = 0;
7819
7820 function onDrop(e) {
7821 var cm = this;
7822 clearDragCursor(cm);
7823 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
7824 { return }
7825 e_preventDefault(e);
7826 if (ie) { lastDrop = +new Date; }
7827 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
7828 if (!pos || cm.isReadOnly()) { return }
7829 // Might be a file drop, in which case we simply extract the text
7830 // and insert it.
7831 if (files && files.length && window.FileReader && window.File) {
7832 var n = files.length, text = Array(n), read = 0;
7833 var loadFile = function (file, i) {
7834 if (cm.options.allowDropFileTypes &&
7835 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
7836 { return }
7837
7838 var reader = new FileReader;
7839 reader.onload = operation(cm, function () {
7840 var content = reader.result;
7841 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
7842 text[i] = content;
7843 if (++read == n) {
7844 pos = clipPos(cm.doc, pos);
7845 var change = {from: pos, to: pos,
7846 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
7847 origin: "paste"};
7848 makeChange(cm.doc, change);
7849 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
7850 }
7851 });
7852 reader.readAsText(file);
7853 };
7854 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
7855 } else { // Normal drop
7856 // Don't do a replace if the drop happened inside of the selected text.
7857 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
7858 cm.state.draggingText(e);
7859 // Ensure the editor is re-focused
7860 setTimeout(function () { return cm.display.input.focus(); }, 20);
7861 return
7862 }
7863 try {
7864 var text$1 = e.dataTransfer.getData("Text");
7865 if (text$1) {
7866 var selected;
7867 if (cm.state.draggingText && !cm.state.draggingText.copy)
7868 { selected = cm.listSelections(); }
7869 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
7870 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
7871 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
7872 cm.replaceSelection(text$1, "around", "paste");
7873 cm.display.input.focus();
7874 }
7875 }
7876 catch(e){}
7877 }
7878 }
7879
7880 function onDragStart(cm, e) {
7881 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
7882 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
7883
7884 e.dataTransfer.setData("Text", cm.getSelection());
7885 e.dataTransfer.effectAllowed = "copyMove";
7886
7887 // Use dummy image instead of default browsers image.
7888 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
7889 if (e.dataTransfer.setDragImage && !safari) {
7890 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
7891 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
7892 if (presto) {
7893 img.width = img.height = 1;
7894 cm.display.wrapper.appendChild(img);
7895 // Force a relayout, or Opera won't use our image for some obscure reason
7896 img._top = img.offsetTop;
7897 }
7898 e.dataTransfer.setDragImage(img, 0, 0);
7899 if (presto) { img.parentNode.removeChild(img); }
7900 }
7901 }
7902
7903 function onDragOver(cm, e) {
7904 var pos = posFromMouse(cm, e);
7905 if (!pos) { return }
7906 var frag = document.createDocumentFragment();
7907 drawSelectionCursor(cm, pos, frag);
7908 if (!cm.display.dragCursor) {
7909 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
7910 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
7911 }
7912 removeChildrenAndAdd(cm.display.dragCursor, frag);
7913 }
7914
7915 function clearDragCursor(cm) {
7916 if (cm.display.dragCursor) {
7917 cm.display.lineSpace.removeChild(cm.display.dragCursor);
7918 cm.display.dragCursor = null;
7919 }
7920 }
7921
7922 // These must be handled carefully, because naively registering a
7923 // handler for each editor will cause the editors to never be
7924 // garbage collected.
7925
7926 function forEachCodeMirror(f) {
7927 if (!document.getElementsByClassName) { return }
7928 var byClass = document.getElementsByClassName("CodeMirror");
7929 for (var i = 0; i < byClass.length; i++) {
7930 var cm = byClass[i].CodeMirror;
7931 if (cm) { f(cm); }
7932 }
7933 }
7934
7935 var globalsRegistered = false;
7936 function ensureGlobalHandlers() {
7937 if (globalsRegistered) { return }
7938 registerGlobalHandlers();
7939 globalsRegistered = true;
7940 }
7941 function registerGlobalHandlers() {
7942 // When the window resizes, we need to refresh active editors.
7943 var resizeTimer;
7944 on(window, "resize", function () {
7945 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
7946 resizeTimer = null;
7947 forEachCodeMirror(onResize);
7948 }, 100); }
7949 });
7950 // When the window loses focus, we want to show the editor as blurred
7951 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
7952 }
7953 // Called when the window resizes
7954 function onResize(cm) {
7955 var d = cm.display;
7956 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
7957 { return }
7958 // Might be a text scaling operation, clear size caches.
7959 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
7960 d.scrollbarsClipped = false;
7961 cm.setSize();
7962 }
7963
7964 var keyNames = {
7965 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
7966 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
7967 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
7968 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
7969 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
7970 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
7971 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
7972 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
7973 };
7974
7975 // Number keys
7976 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
7977 // Alphabetic keys
7978 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
7979 // Function keys
7980 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
7981
7982 var keyMap = {};
7983
7984 keyMap.basic = {
7985 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
7986 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
7987 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
7988 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
7989 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
7990 "Esc": "singleSelection"
7991 };
7992 // Note that the save and find-related commands aren't defined by
7993 // default. User code or addons can define them. Unknown commands
7994 // are simply ignored.
7995 keyMap.pcDefault = {
7996 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
7997 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
7998 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
7999 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8000 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8001 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8002 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8003 fallthrough: "basic"
8004 };
8005 // Very basic readline/emacs-style bindings, which are standard on Mac.
8006 keyMap.emacsy = {
8007 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8008 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8009 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8010 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8011 "Ctrl-O": "openLine"
8012 };
8013 keyMap.macDefault = {
8014 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8015 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8016 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8017 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8018 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8019 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8020 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8021 fallthrough: ["basic", "emacsy"]
8022 };
8023 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8024
8025 // KEYMAP DISPATCH
8026
8027 function normalizeKeyName(name) {
8028 var parts = name.split(/-(?!$)/);
8029 name = parts[parts.length - 1];
8030 var alt, ctrl, shift, cmd;
8031 for (var i = 0; i < parts.length - 1; i++) {
8032 var mod = parts[i];
8033 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8034 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8035 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8036 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8037 else { throw new Error("Unrecognized modifier name: " + mod) }
8038 }
8039 if (alt) { name = "Alt-" + name; }
8040 if (ctrl) { name = "Ctrl-" + name; }
8041 if (cmd) { name = "Cmd-" + name; }
8042 if (shift) { name = "Shift-" + name; }
8043 return name
8044 }
8045
8046 // This is a kludge to keep keymaps mostly working as raw objects
8047 // (backwards compatibility) while at the same time support features
8048 // like normalization and multi-stroke key bindings. It compiles a
8049 // new normalized keymap, and then updates the old object to reflect
8050 // this.
8051 function normalizeKeyMap(keymap) {
8052 var copy = {};
8053 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8054 var value = keymap[keyname];
8055 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8056 if (value == "...") { delete keymap[keyname]; continue }
8057
8058 var keys = map(keyname.split(" "), normalizeKeyName);
8059 for (var i = 0; i < keys.length; i++) {
8060 var val = (void 0), name = (void 0);
8061 if (i == keys.length - 1) {
8062 name = keys.join(" ");
8063 val = value;
8064 } else {
8065 name = keys.slice(0, i + 1).join(" ");
8066 val = "...";
8067 }
8068 var prev = copy[name];
8069 if (!prev) { copy[name] = val; }
8070 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8071 }
8072 delete keymap[keyname];
8073 } }
8074 for (var prop in copy) { keymap[prop] = copy[prop]; }
8075 return keymap
8076 }
8077
8078 function lookupKey(key, map$$1, handle, context) {
8079 map$$1 = getKeyMap(map$$1);
8080 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8081 if (found === false) { return "nothing" }
8082 if (found === "...") { return "multi" }
8083 if (found != null && handle(found)) { return "handled" }
8084
8085 if (map$$1.fallthrough) {
8086 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8087 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8088 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8089 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8090 if (result) { return result }
8091 }
8092 }
8093 }
8094
8095 // Modifier key presses don't count as 'real' key presses for the
8096 // purpose of keymap fallthrough.
8097 function isModifierKey(value) {
8098 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8099 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8100 }
8101
8102 function addModifierNames(name, event, noShift) {
8103 var base = name;
8104 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8105 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8106 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8107 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8108 return name
8109 }
8110
8111 // Look up the name of a key as indicated by an event object.
8112 function keyName(event, noShift) {
8113 if (presto && event.keyCode == 34 && event["char"]) { return false }
8114 var name = keyNames[event.keyCode];
8115 if (name == null || event.altGraphKey) { return false }
8116 return addModifierNames(name, event, noShift)
8117 }
8118
8119 function getKeyMap(val) {
8120 return typeof val == "string" ? keyMap[val] : val
8121 }
8122
8123 // Helper for deleting text near the selection(s), used to implement
8124 // backspace, delete, and similar functionality.
8125 function deleteNearSelection(cm, compute) {
8126 var ranges = cm.doc.sel.ranges, kill = [];
8127 // Build up a set of ranges to kill first, merging overlapping
8128 // ranges.
8129 for (var i = 0; i < ranges.length; i++) {
8130 var toKill = compute(ranges[i]);
8131 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8132 var replaced = kill.pop();
8133 if (cmp(replaced.from, toKill.from) < 0) {
8134 toKill.from = replaced.from;
8135 break
8136 }
8137 }
8138 kill.push(toKill);
8139 }
8140 // Next, remove those actual ranges.
8141 runInOp(cm, function () {
8142 for (var i = kill.length - 1; i >= 0; i--)
8143 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8144 ensureCursorVisible(cm);
8145 });
8146 }
8147
8148 // Commands are parameter-less actions that can be performed on an
8149 // editor, mostly used for keybindings.
8150 var commands = {
8151 selectAll: selectAll,
8152 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8153 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8154 if (range.empty()) {
8155 var len = getLine(cm.doc, range.head.line).text.length;
8156 if (range.head.ch == len && range.head.line < cm.lastLine())
8157 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8158 else
8159 { return {from: range.head, to: Pos(range.head.line, len)} }
8160 } else {
8161 return {from: range.from(), to: range.to()}
8162 }
8163 }); },
8164 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8165 from: Pos(range.from().line, 0),
8166 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8167 }); }); },
8168 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8169 from: Pos(range.from().line, 0), to: range.from()
8170 }); }); },
8171 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8172 var top = cm.charCoords(range.head, "div").top + 5;
8173 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8174 return {from: leftPos, to: range.from()}
8175 }); },
8176 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8177 var top = cm.charCoords(range.head, "div").top + 5;
8178 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8179 return {from: range.from(), to: rightPos }
8180 }); },
8181 undo: function (cm) { return cm.undo(); },
8182 redo: function (cm) { return cm.redo(); },
8183 undoSelection: function (cm) { return cm.undoSelection(); },
8184 redoSelection: function (cm) { return cm.redoSelection(); },
8185 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8186 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8187 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8188 {origin: "+move", bias: 1}
8189 ); },
8190 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8191 {origin: "+move", bias: 1}
8192 ); },
8193 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8194 {origin: "+move", bias: -1}
8195 ); },
8196 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8197 var top = cm.cursorCoords(range.head, "div").top + 5;
8198 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8199 }, sel_move); },
8200 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8201 var top = cm.cursorCoords(range.head, "div").top + 5;
8202 return cm.coordsChar({left: 0, top: top}, "div")
8203 }, sel_move); },
8204 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8205 var top = cm.cursorCoords(range.head, "div").top + 5;
8206 var pos = cm.coordsChar({left: 0, top: top}, "div");
8207 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8208 return pos
8209 }, sel_move); },
8210 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8211 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8212 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8213 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8214 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8215 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8216 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8217 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8218 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8219 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8220 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8221 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8222 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8223 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8224 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8225 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8226 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8227 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8228 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8229 indentMore: function (cm) { return cm.indentSelection("add"); },
8230 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8231 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8232 insertSoftTab: function (cm) {
8233 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8234 for (var i = 0; i < ranges.length; i++) {
8235 var pos = ranges[i].from();
8236 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8237 spaces.push(spaceStr(tabSize - col % tabSize));
8238 }
8239 cm.replaceSelections(spaces);
8240 },
8241 defaultTab: function (cm) {
8242 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8243 else { cm.execCommand("insertTab"); }
8244 },
8245 // Swap the two chars left and right of each selection's head.
8246 // Move cursor behind the two swapped characters afterwards.
8247 //
8248 // Doesn't consider line feeds a character.
8249 // Doesn't scan more than one line above to find a character.
8250 // Doesn't do anything on an empty line.
8251 // Doesn't do anything with non-empty selections.
8252 transposeChars: function (cm) { return runInOp(cm, function () {
8253 var ranges = cm.listSelections(), newSel = [];
8254 for (var i = 0; i < ranges.length; i++) {
8255 if (!ranges[i].empty()) { continue }
8256 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8257 if (line) {
8258 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8259 if (cur.ch > 0) {
8260 cur = new Pos(cur.line, cur.ch + 1);
8261 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8262 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8263 } else if (cur.line > cm.doc.first) {
8264 var prev = getLine(cm.doc, cur.line - 1).text;
8265 if (prev) {
8266 cur = new Pos(cur.line, 1);
8267 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8268 prev.charAt(prev.length - 1),
8269 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8270 }
8271 }
8272 }
8273 newSel.push(new Range(cur, cur));
8274 }
8275 cm.setSelections(newSel);
8276 }); },
8277 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8278 var sels = cm.listSelections();
8279 for (var i = sels.length - 1; i >= 0; i--)
8280 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8281 sels = cm.listSelections();
8282 for (var i$1 = 0; i$1 < sels.length; i$1++)
8283 { cm.indentLine(sels[i$1].from().line, null, true); }
8284 ensureCursorVisible(cm);
8285 }); },
8286 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8287 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8288 };
8289
8290
8291 function lineStart(cm, lineN) {
8292 var line = getLine(cm.doc, lineN);
8293 var visual = visualLine(line);
8294 if (visual != line) { lineN = lineNo(visual); }
8295 return endOfLine(true, cm, visual, lineN, 1)
8296 }
8297 function lineEnd(cm, lineN) {
8298 var line = getLine(cm.doc, lineN);
8299 var visual = visualLineEnd(line);
8300 if (visual != line) { lineN = lineNo(visual); }
8301 return endOfLine(true, cm, line, lineN, -1)
8302 }
8303 function lineStartSmart(cm, pos) {
8304 var start = lineStart(cm, pos.line);
8305 var line = getLine(cm.doc, start.line);
8306 var order = getOrder(line, cm.doc.direction);
8307 if (!order || order[0].level == 0) {
8308 var firstNonWS = Math.max(0, line.text.search(/\S/));
8309 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8310 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8311 }
8312 return start
8313 }
8314
8315 // Run a handler that was bound to a key.
8316 function doHandleBinding(cm, bound, dropShift) {
8317 if (typeof bound == "string") {
8318 bound = commands[bound];
8319 if (!bound) { return false }
8320 }
8321 // Ensure previous input has been read, so that the handler sees a
8322 // consistent view of the document
8323 cm.display.input.ensurePolled();
8324 var prevShift = cm.display.shift, done = false;
8325 try {
8326 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8327 if (dropShift) { cm.display.shift = false; }
8328 done = bound(cm) != Pass;
8329 } finally {
8330 cm.display.shift = prevShift;
8331 cm.state.suppressEdits = false;
8332 }
8333 return done
8334 }
8335
8336 function lookupKeyForEditor(cm, name, handle) {
8337 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8338 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8339 if (result) { return result }
8340 }
8341 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8342 || lookupKey(name, cm.options.keyMap, handle, cm)
8343 }
8344
8345 // Note that, despite the name, this function is also used to check
8346 // for bound mouse clicks.
8347
8348 var stopSeq = new Delayed;
8349 function dispatchKey(cm, name, e, handle) {
8350 var seq = cm.state.keySeq;
8351 if (seq) {
8352 if (isModifierKey(name)) { return "handled" }
8353 stopSeq.set(50, function () {
8354 if (cm.state.keySeq == seq) {
8355 cm.state.keySeq = null;
8356 cm.display.input.reset();
8357 }
8358 });
8359 name = seq + " " + name;
8360 }
8361 var result = lookupKeyForEditor(cm, name, handle);
8362
8363 if (result == "multi")
8364 { cm.state.keySeq = name; }
8365 if (result == "handled")
8366 { signalLater(cm, "keyHandled", cm, name, e); }
8367
8368 if (result == "handled" || result == "multi") {
8369 e_preventDefault(e);
8370 restartBlink(cm);
8371 }
8372
8373 if (seq && !result && /\'$/.test(name)) {
8374 e_preventDefault(e);
8375 return true
8376 }
8377 return !!result
8378 }
8379
8380 // Handle a key from the keydown event.
8381 function handleKeyBinding(cm, e) {
8382 var name = keyName(e, true);
8383 if (!name) { return false }
8384
8385 if (e.shiftKey && !cm.state.keySeq) {
8386 // First try to resolve full name (including 'Shift-'). Failing
8387 // that, see if there is a cursor-motion command (starting with
8388 // 'go') bound to the keyname without 'Shift-'.
8389 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8390 || dispatchKey(cm, name, e, function (b) {
8391 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8392 { return doHandleBinding(cm, b) }
8393 })
8394 } else {
8395 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8396 }
8397 }
8398
8399 // Handle a key from the keypress event
8400 function handleCharBinding(cm, e, ch) {
8401 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8402 }
8403
8404 var lastStoppedKey = null;
8405 function onKeyDown(e) {
8406 var cm = this;
8407 cm.curOp.focus = activeElt();
8408 if (signalDOMEvent(cm, e)) { return }
8409 // IE does strange things with escape.
8410 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8411 var code = e.keyCode;
8412 cm.display.shift = code == 16 || e.shiftKey;
8413 var handled = handleKeyBinding(cm, e);
8414 if (presto) {
8415 lastStoppedKey = handled ? code : null;
8416 // Opera has no cut event... we try to at least catch the key combo
8417 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8418 { cm.replaceSelection("", null, "cut"); }
8419 }
8420
8421 // Turn mouse into crosshair when Alt is held on Mac.
8422 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8423 { showCrossHair(cm); }
8424 }
8425
8426 function showCrossHair(cm) {
8427 var lineDiv = cm.display.lineDiv;
8428 addClass(lineDiv, "CodeMirror-crosshair");
8429
8430 function up(e) {
8431 if (e.keyCode == 18 || !e.altKey) {
8432 rmClass(lineDiv, "CodeMirror-crosshair");
8433 off(document, "keyup", up);
8434 off(document, "mouseover", up);
8435 }
8436 }
8437 on(document, "keyup", up);
8438 on(document, "mouseover", up);
8439 }
8440
8441 function onKeyUp(e) {
8442 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8443 signalDOMEvent(this, e);
8444 }
8445
8446 function onKeyPress(e) {
8447 var cm = this;
8448 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8449 var keyCode = e.keyCode, charCode = e.charCode;
8450 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8451 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8452 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8453 // Some browsers fire keypress events for backspace
8454 if (ch == "\x08") { return }
8455 if (handleCharBinding(cm, e, ch)) { return }
8456 cm.display.input.onKeyPress(e);
8457 }
8458
8459 var DOUBLECLICK_DELAY = 400;
8460
8461 var PastClick = function(time, pos, button) {
8462 this.time = time;
8463 this.pos = pos;
8464 this.button = button;
8465 };
8466
8467 PastClick.prototype.compare = function (time, pos, button) {
8468 return this.time + DOUBLECLICK_DELAY > time &&
8469 cmp(pos, this.pos) == 0 && button == this.button
8470 };
8471
8472 var lastClick;
8473 var lastDoubleClick;
8474 function clickRepeat(pos, button) {
8475 var now = +new Date;
8476 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8477 lastClick = lastDoubleClick = null;
8478 return "triple"
8479 } else if (lastClick && lastClick.compare(now, pos, button)) {
8480 lastDoubleClick = new PastClick(now, pos, button);
8481 lastClick = null;
8482 return "double"
8483 } else {
8484 lastClick = new PastClick(now, pos, button);
8485 lastDoubleClick = null;
8486 return "single"
8487 }
8488 }
8489
8490 // A mouse down can be a single click, double click, triple click,
8491 // start of selection drag, start of text drag, new cursor
8492 // (ctrl-click), rectangle drag (alt-drag), or xwin
8493 // middle-click-paste. Or it might be a click on something we should
8494 // not interfere with, such as a scrollbar or widget.
8495 function onMouseDown(e) {
8496 var cm = this, display = cm.display;
8497 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8498 display.input.ensurePolled();
8499 display.shift = e.shiftKey;
8500
8501 if (eventInWidget(display, e)) {
8502 if (!webkit) {
8503 // Briefly turn off draggability, to allow widgets to do
8504 // normal dragging things.
8505 display.scroller.draggable = false;
8506 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8507 }
8508 return
8509 }
8510 if (clickInGutter(cm, e)) { return }
8511 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8512 window.focus();
8513
8514 // #3261: make sure, that we're not starting a second selection
8515 if (button == 1 && cm.state.selectingText)
8516 { cm.state.selectingText(e); }
8517
8518 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8519
8520 if (button == 1) {
8521 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8522 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8523 } else if (button == 2) {
8524 if (pos) { extendSelection(cm.doc, pos); }
8525 setTimeout(function () { return display.input.focus(); }, 20);
8526 } else if (button == 3) {
8527 if (captureRightClick) { onContextMenu(cm, e); }
8528 else { delayBlurEvent(cm); }
8529 }
8530 }
8531
8532 function handleMappedButton(cm, button, pos, repeat, event) {
8533 var name = "Click";
8534 if (repeat == "double") { name = "Double" + name; }
8535 else if (repeat == "triple") { name = "Triple" + name; }
8536 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8537
8538 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8539 if (typeof bound == "string") { bound = commands[bound]; }
8540 if (!bound) { return false }
8541 var done = false;
8542 try {
8543 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8544 done = bound(cm, pos) != Pass;
8545 } finally {
8546 cm.state.suppressEdits = false;
8547 }
8548 return done
8549 })
8550 }
8551
8552 function configureMouse(cm, repeat, event) {
8553 var option = cm.getOption("configureMouse");
8554 var value = option ? option(cm, repeat, event) : {};
8555 if (value.unit == null) {
8556 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8557 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8558 }
8559 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8560 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8561 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8562 return value
8563 }
8564
8565 function leftButtonDown(cm, pos, repeat, event) {
8566 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8567 else { cm.curOp.focus = activeElt(); }
8568
8569 var behavior = configureMouse(cm, repeat, event);
8570
8571 var sel = cm.doc.sel, contained;
8572 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8573 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8574 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8575 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8576 { leftButtonStartDrag(cm, event, pos, behavior); }
8577 else
8578 { leftButtonSelect(cm, event, pos, behavior); }
8579 }
8580
8581 // Start a text drag. When it ends, see if any dragging actually
8582 // happen, and treat as a click if it didn't.
8583 function leftButtonStartDrag(cm, event, pos, behavior) {
8584 var display = cm.display, moved = false;
8585 var dragEnd = operation(cm, function (e) {
8586 if (webkit) { display.scroller.draggable = false; }
8587 cm.state.draggingText = false;
8588 off(document, "mouseup", dragEnd);
8589 off(document, "mousemove", mouseMove);
8590 off(display.scroller, "dragstart", dragStart);
8591 off(display.scroller, "drop", dragEnd);
8592 if (!moved) {
8593 e_preventDefault(e);
8594 if (!behavior.addNew)
8595 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8596 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8597 if (webkit || ie && ie_version == 9)
8598 { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); }
8599 else
8600 { display.input.focus(); }
8601 }
8602 });
8603 var mouseMove = function(e2) {
8604 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8605 };
8606 var dragStart = function () { return moved = true; };
8607 // Let the drag handler handle this.
8608 if (webkit) { display.scroller.draggable = true; }
8609 cm.state.draggingText = dragEnd;
8610 dragEnd.copy = !behavior.moveOnDrag;
8611 // IE's approach to draggable
8612 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8613 on(document, "mouseup", dragEnd);
8614 on(document, "mousemove", mouseMove);
8615 on(display.scroller, "dragstart", dragStart);
8616 on(display.scroller, "drop", dragEnd);
8617
8618 delayBlurEvent(cm);
8619 setTimeout(function () { return display.input.focus(); }, 20);
8620 }
8621
8622 function rangeForUnit(cm, pos, unit) {
8623 if (unit == "char") { return new Range(pos, pos) }
8624 if (unit == "word") { return cm.findWordAt(pos) }
8625 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
8626 var result = unit(cm, pos);
8627 return new Range(result.from, result.to)
8628 }
8629
8630 // Normal selection, as opposed to text dragging.
8631 function leftButtonSelect(cm, event, start, behavior) {
8632 var display = cm.display, doc = cm.doc;
8633 e_preventDefault(event);
8634
8635 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
8636 if (behavior.addNew && !behavior.extend) {
8637 ourIndex = doc.sel.contains(start);
8638 if (ourIndex > -1)
8639 { ourRange = ranges[ourIndex]; }
8640 else
8641 { ourRange = new Range(start, start); }
8642 } else {
8643 ourRange = doc.sel.primary();
8644 ourIndex = doc.sel.primIndex;
8645 }
8646
8647 if (behavior.unit == "rectangle") {
8648 if (!behavior.addNew) { ourRange = new Range(start, start); }
8649 start = posFromMouse(cm, event, true, true);
8650 ourIndex = -1;
8651 } else {
8652 var range$$1 = rangeForUnit(cm, start, behavior.unit);
8653 if (behavior.extend)
8654 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
8655 else
8656 { ourRange = range$$1; }
8657 }
8658
8659 if (!behavior.addNew) {
8660 ourIndex = 0;
8661 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
8662 startSel = doc.sel;
8663 } else if (ourIndex == -1) {
8664 ourIndex = ranges.length;
8665 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
8666 {scroll: false, origin: "*mouse"});
8667 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
8668 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
8669 {scroll: false, origin: "*mouse"});
8670 startSel = doc.sel;
8671 } else {
8672 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
8673 }
8674
8675 var lastPos = start;
8676 function extendTo(pos) {
8677 if (cmp(lastPos, pos) == 0) { return }
8678 lastPos = pos;
8679
8680 if (behavior.unit == "rectangle") {
8681 var ranges = [], tabSize = cm.options.tabSize;
8682 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
8683 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
8684 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
8685 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
8686 line <= end; line++) {
8687 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
8688 if (left == right)
8689 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
8690 else if (text.length > leftPos)
8691 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
8692 }
8693 if (!ranges.length) { ranges.push(new Range(start, start)); }
8694 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
8695 {origin: "*mouse", scroll: false});
8696 cm.scrollIntoView(pos);
8697 } else {
8698 var oldRange = ourRange;
8699 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
8700 var anchor = oldRange.anchor, head;
8701 if (cmp(range$$1.anchor, anchor) > 0) {
8702 head = range$$1.head;
8703 anchor = minPos(oldRange.from(), range$$1.anchor);
8704 } else {
8705 head = range$$1.anchor;
8706 anchor = maxPos(oldRange.to(), range$$1.head);
8707 }
8708 var ranges$1 = startSel.ranges.slice(0);
8709 ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head);
8710 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse);
8711 }
8712 }
8713
8714 var editorSize = display.wrapper.getBoundingClientRect();
8715 // Used to ensure timeout re-tries don't fire when another extend
8716 // happened in the meantime (clearTimeout isn't reliable -- at
8717 // least on Chrome, the timeouts still happen even when cleared,
8718 // if the clear happens after their scheduled firing time).
8719 var counter = 0;
8720
8721 function extend(e) {
8722 var curCount = ++counter;
8723 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
8724 if (!cur) { return }
8725 if (cmp(cur, lastPos) != 0) {
8726 cm.curOp.focus = activeElt();
8727 extendTo(cur);
8728 var visible = visibleLines(display, doc);
8729 if (cur.line >= visible.to || cur.line < visible.from)
8730 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
8731 } else {
8732 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
8733 if (outside) { setTimeout(operation(cm, function () {
8734 if (counter != curCount) { return }
8735 display.scroller.scrollTop += outside;
8736 extend(e);
8737 }), 50); }
8738 }
8739 }
8740
8741 function done(e) {
8742 cm.state.selectingText = false;
8743 counter = Infinity;
8744 e_preventDefault(e);
8745 display.input.focus();
8746 off(document, "mousemove", move);
8747 off(document, "mouseup", up);
8748 doc.history.lastSelOrigin = null;
8749 }
8750
8751 var move = operation(cm, function (e) {
8752 if (!e_button(e)) { done(e); }
8753 else { extend(e); }
8754 });
8755 var up = operation(cm, done);
8756 cm.state.selectingText = up;
8757 on(document, "mousemove", move);
8758 on(document, "mouseup", up);
8759 }
8760
8761
8762 // Determines whether an event happened in the gutter, and fires the
8763 // handlers for the corresponding event.
8764 function gutterEvent(cm, e, type, prevent) {
8765 var mX, mY;
8766 try { mX = e.clientX; mY = e.clientY; }
8767 catch(e) { return false }
8768 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
8769 if (prevent) { e_preventDefault(e); }
8770
8771 var display = cm.display;
8772 var lineBox = display.lineDiv.getBoundingClientRect();
8773
8774 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
8775 mY -= lineBox.top - display.viewOffset;
8776
8777 for (var i = 0; i < cm.options.gutters.length; ++i) {
8778 var g = display.gutters.childNodes[i];
8779 if (g && g.getBoundingClientRect().right >= mX) {
8780 var line = lineAtHeight(cm.doc, mY);
8781 var gutter = cm.options.gutters[i];
8782 signal(cm, type, cm, line, gutter, e);
8783 return e_defaultPrevented(e)
8784 }
8785 }
8786 }
8787
8788 function clickInGutter(cm, e) {
8789 return gutterEvent(cm, e, "gutterClick", true)
8790 }
8791
8792 // CONTEXT MENU HANDLING
8793
8794 // To make the context menu work, we need to briefly unhide the
8795 // textarea (making it as unobtrusive as possible) to let the
8796 // right-click take effect on it.
8797 function onContextMenu(cm, e) {
8798 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
8799 if (signalDOMEvent(cm, e, "contextmenu")) { return }
8800 cm.display.input.onContextMenu(e);
8801 }
8802
8803 function contextMenuInGutter(cm, e) {
8804 if (!hasHandler(cm, "gutterContextMenu")) { return false }
8805 return gutterEvent(cm, e, "gutterContextMenu", false)
8806 }
8807
8808 function themeChanged(cm) {
8809 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
8810 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
8811 clearCaches(cm);
8812 }
8813
8814 var Init = {toString: function(){return "CodeMirror.Init"}};
8815
8816 var defaults = {};
8817 var optionHandlers = {};
8818
8819 function defineOptions(CodeMirror) {
8820 var optionHandlers = CodeMirror.optionHandlers;
8821
8822 function option(name, deflt, handle, notOnInit) {
8823 CodeMirror.defaults[name] = deflt;
8824 if (handle) { optionHandlers[name] =
8825 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
8826 }
8827
8828 CodeMirror.defineOption = option;
8829
8830 // Passed to option handlers when there is no old value.
8831 CodeMirror.Init = Init;
8832
8833 // These two are, on init, called from the constructor because they
8834 // have to be initialized before the editor can start at all.
8835 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
8836 option("mode", null, function (cm, val) {
8837 cm.doc.modeOption = val;
8838 loadMode(cm);
8839 }, true);
8840
8841 option("indentUnit", 2, loadMode, true);
8842 option("indentWithTabs", false);
8843 option("smartIndent", true);
8844 option("tabSize", 4, function (cm) {
8845 resetModeState(cm);
8846 clearCaches(cm);
8847 regChange(cm);
8848 }, true);
8849 option("lineSeparator", null, function (cm, val) {
8850 cm.doc.lineSep = val;
8851 if (!val) { return }
8852 var newBreaks = [], lineNo = cm.doc.first;
8853 cm.doc.iter(function (line) {
8854 for (var pos = 0;;) {
8855 var found = line.text.indexOf(val, pos);
8856 if (found == -1) { break }
8857 pos = found + val.length;
8858 newBreaks.push(Pos(lineNo, found));
8859 }
8860 lineNo++;
8861 });
8862 for (var i = newBreaks.length - 1; i >= 0; i--)
8863 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
8864 });
8865 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
8866 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
8867 if (old != Init) { cm.refresh(); }
8868 });
8869 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
8870 option("electricChars", true);
8871 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
8872 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
8873 }, true);
8874 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
8875 option("rtlMoveVisually", !windows);
8876 option("wholeLineUpdateBefore", true);
8877
8878 option("theme", "default", function (cm) {
8879 themeChanged(cm);
8880 guttersChanged(cm);
8881 }, true);
8882 option("keyMap", "default", function (cm, val, old) {
8883 var next = getKeyMap(val);
8884 var prev = old != Init && getKeyMap(old);
8885 if (prev && prev.detach) { prev.detach(cm, next); }
8886 if (next.attach) { next.attach(cm, prev || null); }
8887 });
8888 option("extraKeys", null);
8889 option("configureMouse", null);
8890
8891 option("lineWrapping", false, wrappingChanged, true);
8892 option("gutters", [], function (cm) {
8893 setGuttersForLineNumbers(cm.options);
8894 guttersChanged(cm);
8895 }, true);
8896 option("fixedGutter", true, function (cm, val) {
8897 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
8898 cm.refresh();
8899 }, true);
8900 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
8901 option("scrollbarStyle", "native", function (cm) {
8902 initScrollbars(cm);
8903 updateScrollbars(cm);
8904 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
8905 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
8906 }, true);
8907 option("lineNumbers", false, function (cm) {
8908 setGuttersForLineNumbers(cm.options);
8909 guttersChanged(cm);
8910 }, true);
8911 option("firstLineNumber", 1, guttersChanged, true);
8912 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
8913 option("showCursorWhenSelecting", false, updateSelection, true);
8914
8915 option("resetSelectionOnContextMenu", true);
8916 option("lineWiseCopyCut", true);
8917 option("pasteLinesPerSelection", true);
8918
8919 option("readOnly", false, function (cm, val) {
8920 if (val == "nocursor") {
8921 onBlur(cm);
8922 cm.display.input.blur();
8923 }
8924 cm.display.input.readOnlyChanged(val);
8925 });
8926 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
8927 option("dragDrop", true, dragDropChanged);
8928 option("allowDropFileTypes", null);
8929
8930 option("cursorBlinkRate", 530);
8931 option("cursorScrollMargin", 0);
8932 option("cursorHeight", 1, updateSelection, true);
8933 option("singleCursorHeightPerLine", true, updateSelection, true);
8934 option("workTime", 100);
8935 option("workDelay", 100);
8936 option("flattenSpans", true, resetModeState, true);
8937 option("addModeClass", false, resetModeState, true);
8938 option("pollInterval", 100);
8939 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
8940 option("historyEventDelay", 1250);
8941 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
8942 option("maxHighlightLength", 10000, resetModeState, true);
8943 option("moveInputWithCursor", true, function (cm, val) {
8944 if (!val) { cm.display.input.resetPosition(); }
8945 });
8946
8947 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
8948 option("autofocus", null);
8949 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
8950 }
8951
8952 function guttersChanged(cm) {
8953 updateGutters(cm);
8954 regChange(cm);
8955 alignHorizontally(cm);
8956 }
8957
8958 function dragDropChanged(cm, value, old) {
8959 var wasOn = old && old != Init;
8960 if (!value != !wasOn) {
8961 var funcs = cm.display.dragFunctions;
8962 var toggle = value ? on : off;
8963 toggle(cm.display.scroller, "dragstart", funcs.start);
8964 toggle(cm.display.scroller, "dragenter", funcs.enter);
8965 toggle(cm.display.scroller, "dragover", funcs.over);
8966 toggle(cm.display.scroller, "dragleave", funcs.leave);
8967 toggle(cm.display.scroller, "drop", funcs.drop);
8968 }
8969 }
8970
8971 function wrappingChanged(cm) {
8972 if (cm.options.lineWrapping) {
8973 addClass(cm.display.wrapper, "CodeMirror-wrap");
8974 cm.display.sizer.style.minWidth = "";
8975 cm.display.sizerWidth = null;
8976 } else {
8977 rmClass(cm.display.wrapper, "CodeMirror-wrap");
8978 findMaxLine(cm);
8979 }
8980 estimateLineHeights(cm);
8981 regChange(cm);
8982 clearCaches(cm);
8983 setTimeout(function () { return updateScrollbars(cm); }, 100);
8984 }
8985
8986 // A CodeMirror instance represents an editor. This is the object
8987 // that user code is usually dealing with.
8988
8989 function CodeMirror$1(place, options) {
8990 var this$1 = this;
8991
8992 if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
8993
8994 this.options = options = options ? copyObj(options) : {};
8995 // Determine effective options based on given values and defaults.
8996 copyObj(defaults, options, false);
8997 setGuttersForLineNumbers(options);
8998
8999 var doc = options.value;
9000 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9001 this.doc = doc;
9002
9003 var input = new CodeMirror$1.inputStyles[options.inputStyle](this);
9004 var display = this.display = new Display(place, doc, input);
9005 display.wrapper.CodeMirror = this;
9006 updateGutters(this);
9007 themeChanged(this);
9008 if (options.lineWrapping)
9009 { this.display.wrapper.className += " CodeMirror-wrap"; }
9010 initScrollbars(this);
9011
9012 this.state = {
9013 keyMaps: [], // stores maps added by addKeyMap
9014 overlays: [], // highlighting overlays, as added by addOverlay
9015 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9016 overwrite: false,
9017 delayingBlurEvent: false,
9018 focused: false,
9019 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9020 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
9021 selectingText: false,
9022 draggingText: false,
9023 highlight: new Delayed(), // stores highlight worker timeout
9024 keySeq: null, // Unfinished key sequence
9025 specialChars: null
9026 };
9027
9028 if (options.autofocus && !mobile) { display.input.focus(); }
9029
9030 // Override magic textarea content restore that IE sometimes does
9031 // on our hidden textarea on reload
9032 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9033
9034 registerEventHandlers(this);
9035 ensureGlobalHandlers();
9036
9037 startOperation(this);
9038 this.curOp.forceUpdate = true;
9039 attachDoc(this, doc);
9040
9041 if ((options.autofocus && !mobile) || this.hasFocus())
9042 { setTimeout(bind(onFocus, this), 20); }
9043 else
9044 { onBlur(this); }
9045
9046 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9047 { optionHandlers[opt](this$1, options[opt], Init); } }
9048 maybeUpdateLineNumberWidth(this);
9049 if (options.finishInit) { options.finishInit(this); }
9050 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9051 endOperation(this);
9052 // Suppress optimizelegibility in Webkit, since it breaks text
9053 // measuring on line wrapping boundaries.
9054 if (webkit && options.lineWrapping &&
9055 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9056 { display.lineDiv.style.textRendering = "auto"; }
9057 }
9058
9059 // The default configuration options.
9060 CodeMirror$1.defaults = defaults;
9061 // Functions to run when options are changed.
9062 CodeMirror$1.optionHandlers = optionHandlers;
9063
9064 // Attach the necessary event handlers when initializing the editor
9065 function registerEventHandlers(cm) {
9066 var d = cm.display;
9067 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9068 // Older IE's will not fire a second mousedown for a double click
9069 if (ie && ie_version < 11)
9070 { on(d.scroller, "dblclick", operation(cm, function (e) {
9071 if (signalDOMEvent(cm, e)) { return }
9072 var pos = posFromMouse(cm, e);
9073 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9074 e_preventDefault(e);
9075 var word = cm.findWordAt(pos);
9076 extendSelection(cm.doc, word.anchor, word.head);
9077 })); }
9078 else
9079 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9080 // Some browsers fire contextmenu *after* opening the menu, at
9081 // which point we can't mess with it anymore. Context menu is
9082 // handled in onMouseDown for these browsers.
9083 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); }
9084
9085 // Used to suppress mouse event handling when a touch happens
9086 var touchFinished, prevTouch = {end: 0};
9087 function finishTouch() {
9088 if (d.activeTouch) {
9089 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9090 prevTouch = d.activeTouch;
9091 prevTouch.end = +new Date;
9092 }
9093 }
9094 function isMouseLikeTouchEvent(e) {
9095 if (e.touches.length != 1) { return false }
9096 var touch = e.touches[0];
9097 return touch.radiusX <= 1 && touch.radiusY <= 1
9098 }
9099 function farAway(touch, other) {
9100 if (other.left == null) { return true }
9101 var dx = other.left - touch.left, dy = other.top - touch.top;
9102 return dx * dx + dy * dy > 20 * 20
9103 }
9104 on(d.scroller, "touchstart", function (e) {
9105 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
9106 d.input.ensurePolled();
9107 clearTimeout(touchFinished);
9108 var now = +new Date;
9109 d.activeTouch = {start: now, moved: false,
9110 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9111 if (e.touches.length == 1) {
9112 d.activeTouch.left = e.touches[0].pageX;
9113 d.activeTouch.top = e.touches[0].pageY;
9114 }
9115 }
9116 });
9117 on(d.scroller, "touchmove", function () {
9118 if (d.activeTouch) { d.activeTouch.moved = true; }
9119 });
9120 on(d.scroller, "touchend", function (e) {
9121 var touch = d.activeTouch;
9122 if (touch && !eventInWidget(d, e) && touch.left != null &&
9123 !touch.moved && new Date - touch.start < 300) {
9124 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9125 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9126 { range = new Range(pos, pos); }
9127 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9128 { range = cm.findWordAt(pos); }
9129 else // Triple tap
9130 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9131 cm.setSelection(range.anchor, range.head);
9132 cm.focus();
9133 e_preventDefault(e);
9134 }
9135 finishTouch();
9136 });
9137 on(d.scroller, "touchcancel", finishTouch);
9138
9139 // Sync scrolling between fake scrollbars and real scrollable
9140 // area, ensure viewport is updated when scrolling.
9141 on(d.scroller, "scroll", function () {
9142 if (d.scroller.clientHeight) {
9143 updateScrollTop(cm, d.scroller.scrollTop);
9144 setScrollLeft(cm, d.scroller.scrollLeft, true);
9145 signal(cm, "scroll", cm);
9146 }
9147 });
9148
9149 // Listen to wheel events in order to try and update the viewport on time.
9150 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9151 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9152
9153 // Prevent wrapper from ever scrolling
9154 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9155
9156 d.dragFunctions = {
9157 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9158 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9159 start: function (e) { return onDragStart(cm, e); },
9160 drop: operation(cm, onDrop),
9161 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9162 };
9163
9164 var inp = d.input.getField();
9165 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9166 on(inp, "keydown", operation(cm, onKeyDown));
9167 on(inp, "keypress", operation(cm, onKeyPress));
9168 on(inp, "focus", function (e) { return onFocus(cm, e); });
9169 on(inp, "blur", function (e) { return onBlur(cm, e); });
9170 }
9171
9172 var initHooks = [];
9173 CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); };
9174
9175 // Indent the given line. The how parameter can be "smart",
9176 // "add"/null, "subtract", or "prev". When aggressive is false
9177 // (typically set to true for forced single-line indents), empty
9178 // lines are not indented, and places where the mode returns Pass
9179 // are left alone.
9180 function indentLine(cm, n, how, aggressive) {
9181 var doc = cm.doc, state;
9182 if (how == null) { how = "add"; }
9183 if (how == "smart") {
9184 // Fall back to "prev" when the mode doesn't have an indentation
9185 // method.
9186 if (!doc.mode.indent) { how = "prev"; }
9187 else { state = getContextBefore(cm, n).state; }
9188 }
9189
9190 var tabSize = cm.options.tabSize;
9191 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9192 if (line.stateAfter) { line.stateAfter = null; }
9193 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9194 if (!aggressive && !/\S/.test(line.text)) {
9195 indentation = 0;
9196 how = "not";
9197 } else if (how == "smart") {
9198 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9199 if (indentation == Pass || indentation > 150) {
9200 if (!aggressive) { return }
9201 how = "prev";
9202 }
9203 }
9204 if (how == "prev") {
9205 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9206 else { indentation = 0; }
9207 } else if (how == "add") {
9208 indentation = curSpace + cm.options.indentUnit;
9209 } else if (how == "subtract") {
9210 indentation = curSpace - cm.options.indentUnit;
9211 } else if (typeof how == "number") {
9212 indentation = curSpace + how;
9213 }
9214 indentation = Math.max(0, indentation);
9215
9216 var indentString = "", pos = 0;
9217 if (cm.options.indentWithTabs)
9218 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9219 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9220
9221 if (indentString != curSpaceString) {
9222 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9223 line.stateAfter = null;
9224 return true
9225 } else {
9226 // Ensure that, if the cursor was in the whitespace at the start
9227 // of the line, it is moved to the end of that space.
9228 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9229 var range = doc.sel.ranges[i$1];
9230 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9231 var pos$1 = Pos(n, curSpaceString.length);
9232 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9233 break
9234 }
9235 }
9236 }
9237 }
9238
9239 // This will be set to a {lineWise: bool, text: [string]} object, so
9240 // that, when pasting, we know what kind of selections the copied
9241 // text was made out of.
9242 var lastCopied = null;
9243
9244 function setLastCopied(newLastCopied) {
9245 lastCopied = newLastCopied;
9246 }
9247
9248 function applyTextInput(cm, inserted, deleted, sel, origin) {
9249 var doc = cm.doc;
9250 cm.display.shift = false;
9251 if (!sel) { sel = doc.sel; }
9252
9253 var paste = cm.state.pasteIncoming || origin == "paste";
9254 var textLines = splitLinesAuto(inserted), multiPaste = null;
9255 // When pasing N lines into N selections, insert one line per selection
9256 if (paste && sel.ranges.length > 1) {
9257 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9258 if (sel.ranges.length % lastCopied.text.length == 0) {
9259 multiPaste = [];
9260 for (var i = 0; i < lastCopied.text.length; i++)
9261 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9262 }
9263 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9264 multiPaste = map(textLines, function (l) { return [l]; });
9265 }
9266 }
9267
9268 var updateInput;
9269 // Normal behavior is to insert the new text into every selection
9270 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9271 var range$$1 = sel.ranges[i$1];
9272 var from = range$$1.from(), to = range$$1.to();
9273 if (range$$1.empty()) {
9274 if (deleted && deleted > 0) // Handle deletion
9275 { from = Pos(from.line, from.ch - deleted); }
9276 else if (cm.state.overwrite && !paste) // Handle overwrite
9277 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9278 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9279 { from = to = Pos(from.line, 0); }
9280 }
9281 updateInput = cm.curOp.updateInput;
9282 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9283 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
9284 makeChange(cm.doc, changeEvent);
9285 signalLater(cm, "inputRead", cm, changeEvent);
9286 }
9287 if (inserted && !paste)
9288 { triggerElectric(cm, inserted); }
9289
9290 ensureCursorVisible(cm);
9291 cm.curOp.updateInput = updateInput;
9292 cm.curOp.typing = true;
9293 cm.state.pasteIncoming = cm.state.cutIncoming = false;
9294 }
9295
9296 function handlePaste(e, cm) {
9297 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9298 if (pasted) {
9299 e.preventDefault();
9300 if (!cm.isReadOnly() && !cm.options.disableInput)
9301 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9302 return true
9303 }
9304 }
9305
9306 function triggerElectric(cm, inserted) {
9307 // When an 'electric' character is inserted, immediately trigger a reindent
9308 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9309 var sel = cm.doc.sel;
9310
9311 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9312 var range$$1 = sel.ranges[i];
9313 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9314 var mode = cm.getModeAt(range$$1.head);
9315 var indented = false;
9316 if (mode.electricChars) {
9317 for (var j = 0; j < mode.electricChars.length; j++)
9318 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9319 indented = indentLine(cm, range$$1.head.line, "smart");
9320 break
9321 } }
9322 } else if (mode.electricInput) {
9323 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9324 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9325 }
9326 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9327 }
9328 }
9329
9330 function copyableRanges(cm) {
9331 var text = [], ranges = [];
9332 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9333 var line = cm.doc.sel.ranges[i].head.line;
9334 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9335 ranges.push(lineRange);
9336 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9337 }
9338 return {text: text, ranges: ranges}
9339 }
9340
9341 function disableBrowserMagic(field, spellcheck) {
9342 field.setAttribute("autocorrect", "off");
9343 field.setAttribute("autocapitalize", "off");
9344 field.setAttribute("spellcheck", !!spellcheck);
9345 }
9346
9347 function hiddenTextarea() {
9348 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9349 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9350 // The textarea is kept positioned near the cursor to prevent the
9351 // fact that it'll be scrolled into view on input from scrolling
9352 // our fake cursor out of view. On webkit, when wrap=off, paste is
9353 // very slow. So make the area wide instead.
9354 if (webkit) { te.style.width = "1000px"; }
9355 else { te.setAttribute("wrap", "off"); }
9356 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9357 if (ios) { te.style.border = "1px solid black"; }
9358 disableBrowserMagic(te);
9359 return div
9360 }
9361
9362 // The publicly visible API. Note that methodOp(f) means
9363 // 'wrap f in an operation, performed on its `this` parameter'.
9364
9365 // This is not the complete set of editor methods. Most of the
9366 // methods defined on the Doc type are also injected into
9367 // CodeMirror.prototype, for backwards compatibility and
9368 // convenience.
9369
9370 var addEditorMethods = function(CodeMirror) {
9371 var optionHandlers = CodeMirror.optionHandlers;
9372
9373 var helpers = CodeMirror.helpers = {};
9374
9375 CodeMirror.prototype = {
9376 constructor: CodeMirror,
9377 focus: function(){window.focus(); this.display.input.focus();},
9378
9379 setOption: function(option, value) {
9380 var options = this.options, old = options[option];
9381 if (options[option] == value && option != "mode") { return }
9382 options[option] = value;
9383 if (optionHandlers.hasOwnProperty(option))
9384 { operation(this, optionHandlers[option])(this, value, old); }
9385 signal(this, "optionChange", this, option);
9386 },
9387
9388 getOption: function(option) {return this.options[option]},
9389 getDoc: function() {return this.doc},
9390
9391 addKeyMap: function(map$$1, bottom) {
9392 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9393 },
9394 removeKeyMap: function(map$$1) {
9395 var maps = this.state.keyMaps;
9396 for (var i = 0; i < maps.length; ++i)
9397 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9398 maps.splice(i, 1);
9399 return true
9400 } }
9401 },
9402
9403 addOverlay: methodOp(function(spec, options) {
9404 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9405 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9406 insertSorted(this.state.overlays,
9407 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9408 priority: (options && options.priority) || 0},
9409 function (overlay) { return overlay.priority; });
9410 this.state.modeGen++;
9411 regChange(this);
9412 }),
9413 removeOverlay: methodOp(function(spec) {
9414 var this$1 = this;
9415
9416 var overlays = this.state.overlays;
9417 for (var i = 0; i < overlays.length; ++i) {
9418 var cur = overlays[i].modeSpec;
9419 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9420 overlays.splice(i, 1);
9421 this$1.state.modeGen++;
9422 regChange(this$1);
9423 return
9424 }
9425 }
9426 }),
9427
9428 indentLine: methodOp(function(n, dir, aggressive) {
9429 if (typeof dir != "string" && typeof dir != "number") {
9430 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9431 else { dir = dir ? "add" : "subtract"; }
9432 }
9433 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9434 }),
9435 indentSelection: methodOp(function(how) {
9436 var this$1 = this;
9437
9438 var ranges = this.doc.sel.ranges, end = -1;
9439 for (var i = 0; i < ranges.length; i++) {
9440 var range$$1 = ranges[i];
9441 if (!range$$1.empty()) {
9442 var from = range$$1.from(), to = range$$1.to();
9443 var start = Math.max(end, from.line);
9444 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9445 for (var j = start; j < end; ++j)
9446 { indentLine(this$1, j, how); }
9447 var newRanges = this$1.doc.sel.ranges;
9448 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9449 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9450 } else if (range$$1.head.line > end) {
9451 indentLine(this$1, range$$1.head.line, how, true);
9452 end = range$$1.head.line;
9453 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9454 }
9455 }
9456 }),
9457
9458 // Fetch the parser token for a given character. Useful for hacks
9459 // that want to inspect the mode state (say, for completion).
9460 getTokenAt: function(pos, precise) {
9461 return takeToken(this, pos, precise)
9462 },
9463
9464 getLineTokens: function(line, precise) {
9465 return takeToken(this, Pos(line), precise, true)
9466 },
9467
9468 getTokenTypeAt: function(pos) {
9469 pos = clipPos(this.doc, pos);
9470 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9471 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9472 var type;
9473 if (ch == 0) { type = styles[2]; }
9474 else { for (;;) {
9475 var mid = (before + after) >> 1;
9476 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9477 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9478 else { type = styles[mid * 2 + 2]; break }
9479 } }
9480 var cut = type ? type.indexOf("overlay ") : -1;
9481 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9482 },
9483
9484 getModeAt: function(pos) {
9485 var mode = this.doc.mode;
9486 if (!mode.innerMode) { return mode }
9487 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9488 },
9489
9490 getHelper: function(pos, type) {
9491 return this.getHelpers(pos, type)[0]
9492 },
9493
9494 getHelpers: function(pos, type) {
9495 var this$1 = this;
9496
9497 var found = [];
9498 if (!helpers.hasOwnProperty(type)) { return found }
9499 var help = helpers[type], mode = this.getModeAt(pos);
9500 if (typeof mode[type] == "string") {
9501 if (help[mode[type]]) { found.push(help[mode[type]]); }
9502 } else if (mode[type]) {
9503 for (var i = 0; i < mode[type].length; i++) {
9504 var val = help[mode[type][i]];
9505 if (val) { found.push(val); }
9506 }
9507 } else if (mode.helperType && help[mode.helperType]) {
9508 found.push(help[mode.helperType]);
9509 } else if (help[mode.name]) {
9510 found.push(help[mode.name]);
9511 }
9512 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9513 var cur = help._global[i$1];
9514 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9515 { found.push(cur.val); }
9516 }
9517 return found
9518 },
9519
9520 getStateAfter: function(line, precise) {
9521 var doc = this.doc;
9522 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9523 return getContextBefore(this, line + 1, precise).state
9524 },
9525
9526 cursorCoords: function(start, mode) {
9527 var pos, range$$1 = this.doc.sel.primary();
9528 if (start == null) { pos = range$$1.head; }
9529 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9530 else { pos = start ? range$$1.from() : range$$1.to(); }
9531 return cursorCoords(this, pos, mode || "page")
9532 },
9533
9534 charCoords: function(pos, mode) {
9535 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9536 },
9537
9538 coordsChar: function(coords, mode) {
9539 coords = fromCoordSystem(this, coords, mode || "page");
9540 return coordsChar(this, coords.left, coords.top)
9541 },
9542
9543 lineAtHeight: function(height, mode) {
9544 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9545 return lineAtHeight(this.doc, height + this.display.viewOffset)
9546 },
9547 heightAtLine: function(line, mode, includeWidgets) {
9548 var end = false, lineObj;
9549 if (typeof line == "number") {
9550 var last = this.doc.first + this.doc.size - 1;
9551 if (line < this.doc.first) { line = this.doc.first; }
9552 else if (line > last) { line = last; end = true; }
9553 lineObj = getLine(this.doc, line);
9554 } else {
9555 lineObj = line;
9556 }
9557 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9558 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9559 },
9560
9561 defaultTextHeight: function() { return textHeight(this.display) },
9562 defaultCharWidth: function() { return charWidth(this.display) },
9563
9564 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9565
9566 addWidget: function(pos, node, scroll, vert, horiz) {
9567 var display = this.display;
9568 pos = cursorCoords(this, clipPos(this.doc, pos));
9569 var top = pos.bottom, left = pos.left;
9570 node.style.position = "absolute";
9571 node.setAttribute("cm-ignore-events", "true");
9572 this.display.input.setUneditable(node);
9573 display.sizer.appendChild(node);
9574 if (vert == "over") {
9575 top = pos.top;
9576 } else if (vert == "above" || vert == "near") {
9577 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9578 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
9579 // Default to positioning above (if specified and possible); otherwise default to positioning below
9580 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
9581 { top = pos.top - node.offsetHeight; }
9582 else if (pos.bottom + node.offsetHeight <= vspace)
9583 { top = pos.bottom; }
9584 if (left + node.offsetWidth > hspace)
9585 { left = hspace - node.offsetWidth; }
9586 }
9587 node.style.top = top + "px";
9588 node.style.left = node.style.right = "";
9589 if (horiz == "right") {
9590 left = display.sizer.clientWidth - node.offsetWidth;
9591 node.style.right = "0px";
9592 } else {
9593 if (horiz == "left") { left = 0; }
9594 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
9595 node.style.left = left + "px";
9596 }
9597 if (scroll)
9598 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
9599 },
9600
9601 triggerOnKeyDown: methodOp(onKeyDown),
9602 triggerOnKeyPress: methodOp(onKeyPress),
9603 triggerOnKeyUp: onKeyUp,
9604 triggerOnMouseDown: methodOp(onMouseDown),
9605
9606 execCommand: function(cmd) {
9607 if (commands.hasOwnProperty(cmd))
9608 { return commands[cmd].call(null, this) }
9609 },
9610
9611 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
9612
9613 findPosH: function(from, amount, unit, visually) {
9614 var this$1 = this;
9615
9616 var dir = 1;
9617 if (amount < 0) { dir = -1; amount = -amount; }
9618 var cur = clipPos(this.doc, from);
9619 for (var i = 0; i < amount; ++i) {
9620 cur = findPosH(this$1.doc, cur, dir, unit, visually);
9621 if (cur.hitSide) { break }
9622 }
9623 return cur
9624 },
9625
9626 moveH: methodOp(function(dir, unit) {
9627 var this$1 = this;
9628
9629 this.extendSelectionsBy(function (range$$1) {
9630 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
9631 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
9632 else
9633 { return dir < 0 ? range$$1.from() : range$$1.to() }
9634 }, sel_move);
9635 }),
9636
9637 deleteH: methodOp(function(dir, unit) {
9638 var sel = this.doc.sel, doc = this.doc;
9639 if (sel.somethingSelected())
9640 { doc.replaceSelection("", null, "+delete"); }
9641 else
9642 { deleteNearSelection(this, function (range$$1) {
9643 var other = findPosH(doc, range$$1.head, dir, unit, false);
9644 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
9645 }); }
9646 }),
9647
9648 findPosV: function(from, amount, unit, goalColumn) {
9649 var this$1 = this;
9650
9651 var dir = 1, x = goalColumn;
9652 if (amount < 0) { dir = -1; amount = -amount; }
9653 var cur = clipPos(this.doc, from);
9654 for (var i = 0; i < amount; ++i) {
9655 var coords = cursorCoords(this$1, cur, "div");
9656 if (x == null) { x = coords.left; }
9657 else { coords.left = x; }
9658 cur = findPosV(this$1, coords, dir, unit);
9659 if (cur.hitSide) { break }
9660 }
9661 return cur
9662 },
9663
9664 moveV: methodOp(function(dir, unit) {
9665 var this$1 = this;
9666
9667 var doc = this.doc, goals = [];
9668 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
9669 doc.extendSelectionsBy(function (range$$1) {
9670 if (collapse)
9671 { return dir < 0 ? range$$1.from() : range$$1.to() }
9672 var headPos = cursorCoords(this$1, range$$1.head, "div");
9673 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
9674 goals.push(headPos.left);
9675 var pos = findPosV(this$1, headPos, dir, unit);
9676 if (unit == "page" && range$$1 == doc.sel.primary())
9677 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
9678 return pos
9679 }, sel_move);
9680 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
9681 { doc.sel.ranges[i].goalColumn = goals[i]; } }
9682 }),
9683
9684 // Find the word at the given position (as returned by coordsChar).
9685 findWordAt: function(pos) {
9686 var doc = this.doc, line = getLine(doc, pos.line).text;
9687 var start = pos.ch, end = pos.ch;
9688 if (line) {
9689 var helper = this.getHelper(pos, "wordChars");
9690 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
9691 var startChar = line.charAt(start);
9692 var check = isWordChar(startChar, helper)
9693 ? function (ch) { return isWordChar(ch, helper); }
9694 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
9695 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
9696 while (start > 0 && check(line.charAt(start - 1))) { --start; }
9697 while (end < line.length && check(line.charAt(end))) { ++end; }
9698 }
9699 return new Range(Pos(pos.line, start), Pos(pos.line, end))
9700 },
9701
9702 toggleOverwrite: function(value) {
9703 if (value != null && value == this.state.overwrite) { return }
9704 if (this.state.overwrite = !this.state.overwrite)
9705 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
9706 else
9707 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
9708
9709 signal(this, "overwriteToggle", this, this.state.overwrite);
9710 },
9711 hasFocus: function() { return this.display.input.getField() == activeElt() },
9712 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
9713
9714 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
9715 getScrollInfo: function() {
9716 var scroller = this.display.scroller;
9717 return {left: scroller.scrollLeft, top: scroller.scrollTop,
9718 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
9719 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
9720 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
9721 },
9722
9723 scrollIntoView: methodOp(function(range$$1, margin) {
9724 if (range$$1 == null) {
9725 range$$1 = {from: this.doc.sel.primary().head, to: null};
9726 if (margin == null) { margin = this.options.cursorScrollMargin; }
9727 } else if (typeof range$$1 == "number") {
9728 range$$1 = {from: Pos(range$$1, 0), to: null};
9729 } else if (range$$1.from == null) {
9730 range$$1 = {from: range$$1, to: null};
9731 }
9732 if (!range$$1.to) { range$$1.to = range$$1.from; }
9733 range$$1.margin = margin || 0;
9734
9735 if (range$$1.from.line != null) {
9736 scrollToRange(this, range$$1);
9737 } else {
9738 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
9739 }
9740 }),
9741
9742 setSize: methodOp(function(width, height) {
9743 var this$1 = this;
9744
9745 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
9746 if (width != null) { this.display.wrapper.style.width = interpret(width); }
9747 if (height != null) { this.display.wrapper.style.height = interpret(height); }
9748 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
9749 var lineNo$$1 = this.display.viewFrom;
9750 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
9751 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
9752 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
9753 ++lineNo$$1;
9754 });
9755 this.curOp.forceUpdate = true;
9756 signal(this, "refresh", this);
9757 }),
9758
9759 operation: function(f){return runInOp(this, f)},
9760 startOperation: function(){return startOperation(this)},
9761 endOperation: function(){return endOperation(this)},
9762
9763 refresh: methodOp(function() {
9764 var oldHeight = this.display.cachedTextHeight;
9765 regChange(this);
9766 this.curOp.forceUpdate = true;
9767 clearCaches(this);
9768 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
9769 updateGutterSpace(this);
9770 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
9771 { estimateLineHeights(this); }
9772 signal(this, "refresh", this);
9773 }),
9774
9775 swapDoc: methodOp(function(doc) {
9776 var old = this.doc;
9777 old.cm = null;
9778 attachDoc(this, doc);
9779 clearCaches(this);
9780 this.display.input.reset();
9781 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
9782 this.curOp.forceScroll = true;
9783 signalLater(this, "swapDoc", this, old);
9784 return old
9785 }),
9786
9787 getInputField: function(){return this.display.input.getField()},
9788 getWrapperElement: function(){return this.display.wrapper},
9789 getScrollerElement: function(){return this.display.scroller},
9790 getGutterElement: function(){return this.display.gutters}
9791 };
9792 eventMixin(CodeMirror);
9793
9794 CodeMirror.registerHelper = function(type, name, value) {
9795 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
9796 helpers[type][name] = value;
9797 };
9798 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
9799 CodeMirror.registerHelper(type, name, value);
9800 helpers[type]._global.push({pred: predicate, val: value});
9801 };
9802 };
9803
9804 // Used for horizontal relative motion. Dir is -1 or 1 (left or
9805 // right), unit can be "char", "column" (like char, but doesn't
9806 // cross line boundaries), "word" (across next word), or "group" (to
9807 // the start of next group of word or non-word-non-whitespace
9808 // chars). The visually param controls whether, in right-to-left
9809 // text, direction 1 means to move towards the next index in the
9810 // string, or towards the character to the right of the current
9811 // position. The resulting position will have a hitSide=true
9812 // property if it reached the end of the document.
9813 function findPosH(doc, pos, dir, unit, visually) {
9814 var oldPos = pos;
9815 var origDir = dir;
9816 var lineObj = getLine(doc, pos.line);
9817 function findNextLine() {
9818 var l = pos.line + dir;
9819 if (l < doc.first || l >= doc.first + doc.size) { return false }
9820 pos = new Pos(l, pos.ch, pos.sticky);
9821 return lineObj = getLine(doc, l)
9822 }
9823 function moveOnce(boundToLine) {
9824 var next;
9825 if (visually) {
9826 next = moveVisually(doc.cm, lineObj, pos, dir);
9827 } else {
9828 next = moveLogically(lineObj, pos, dir);
9829 }
9830 if (next == null) {
9831 if (!boundToLine && findNextLine())
9832 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
9833 else
9834 { return false }
9835 } else {
9836 pos = next;
9837 }
9838 return true
9839 }
9840
9841 if (unit == "char") {
9842 moveOnce();
9843 } else if (unit == "column") {
9844 moveOnce(true);
9845 } else if (unit == "word" || unit == "group") {
9846 var sawType = null, group = unit == "group";
9847 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
9848 for (var first = true;; first = false) {
9849 if (dir < 0 && !moveOnce(!first)) { break }
9850 var cur = lineObj.text.charAt(pos.ch) || "\n";
9851 var type = isWordChar(cur, helper) ? "w"
9852 : group && cur == "\n" ? "n"
9853 : !group || /\s/.test(cur) ? null
9854 : "p";
9855 if (group && !first && !type) { type = "s"; }
9856 if (sawType && sawType != type) {
9857 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
9858 break
9859 }
9860
9861 if (type) { sawType = type; }
9862 if (dir > 0 && !moveOnce(!first)) { break }
9863 }
9864 }
9865 var result = skipAtomic(doc, pos, oldPos, origDir, true);
9866 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
9867 return result
9868 }
9869
9870 // For relative vertical movement. Dir may be -1 or 1. Unit can be
9871 // "page" or "line". The resulting position will have a hitSide=true
9872 // property if it reached the end of the document.
9873 function findPosV(cm, pos, dir, unit) {
9874 var doc = cm.doc, x = pos.left, y;
9875 if (unit == "page") {
9876 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
9877 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
9878 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
9879
9880 } else if (unit == "line") {
9881 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
9882 }
9883 var target;
9884 for (;;) {
9885 target = coordsChar(cm, x, y);
9886 if (!target.outside) { break }
9887 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
9888 y += dir * 5;
9889 }
9890 return target
9891 }
9892
9893 // CONTENTEDITABLE INPUT STYLE
9894
9895 var ContentEditableInput = function(cm) {
9896 this.cm = cm;
9897 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
9898 this.polling = new Delayed();
9899 this.composing = null;
9900 this.gracePeriod = false;
9901 this.readDOMTimeout = null;
9902 };
9903
9904 ContentEditableInput.prototype.init = function (display) {
9905 var this$1 = this;
9906
9907 var input = this, cm = input.cm;
9908 var div = input.div = display.lineDiv;
9909 disableBrowserMagic(div, cm.options.spellcheck);
9910
9911 on(div, "paste", function (e) {
9912 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9913 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
9914 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
9915 });
9916
9917 on(div, "compositionstart", function (e) {
9918 this$1.composing = {data: e.data, done: false};
9919 });
9920 on(div, "compositionupdate", function (e) {
9921 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
9922 });
9923 on(div, "compositionend", function (e) {
9924 if (this$1.composing) {
9925 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
9926 this$1.composing.done = true;
9927 }
9928 });
9929
9930 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
9931
9932 on(div, "input", function () {
9933 if (!this$1.composing) { this$1.readFromDOMSoon(); }
9934 });
9935
9936 function onCopyCut(e) {
9937 if (signalDOMEvent(cm, e)) { return }
9938 if (cm.somethingSelected()) {
9939 setLastCopied({lineWise: false, text: cm.getSelections()});
9940 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
9941 } else if (!cm.options.lineWiseCopyCut) {
9942 return
9943 } else {
9944 var ranges = copyableRanges(cm);
9945 setLastCopied({lineWise: true, text: ranges.text});
9946 if (e.type == "cut") {
9947 cm.operation(function () {
9948 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
9949 cm.replaceSelection("", null, "cut");
9950 });
9951 }
9952 }
9953 if (e.clipboardData) {
9954 e.clipboardData.clearData();
9955 var content = lastCopied.text.join("\n");
9956 // iOS exposes the clipboard API, but seems to discard content inserted into it
9957 e.clipboardData.setData("Text", content);
9958 if (e.clipboardData.getData("Text") == content) {
9959 e.preventDefault();
9960 return
9961 }
9962 }
9963 // Old-fashioned briefly-focus-a-textarea hack
9964 var kludge = hiddenTextarea(), te = kludge.firstChild;
9965 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
9966 te.value = lastCopied.text.join("\n");
9967 var hadFocus = document.activeElement;
9968 selectInput(te);
9969 setTimeout(function () {
9970 cm.display.lineSpace.removeChild(kludge);
9971 hadFocus.focus();
9972 if (hadFocus == div) { input.showPrimarySelection(); }
9973 }, 50);
9974 }
9975 on(div, "copy", onCopyCut);
9976 on(div, "cut", onCopyCut);
9977 };
9978
9979 ContentEditableInput.prototype.prepareSelection = function () {
9980 var result = prepareSelection(this.cm, false);
9981 result.focus = this.cm.state.focused;
9982 return result
9983 };
9984
9985 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
9986 if (!info || !this.cm.display.view.length) { return }
9987 if (info.focus || takeFocus) { this.showPrimarySelection(); }
9988 this.showMultipleSelections(info);
9989 };
9990
9991 ContentEditableInput.prototype.showPrimarySelection = function () {
9992 var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
9993 var from = prim.from(), to = prim.to();
9994
9995 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
9996 sel.removeAllRanges();
9997 return
9998 }
9999
10000 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10001 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10002 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10003 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10004 cmp(maxPos(curAnchor, curFocus), to) == 0)
10005 { return }
10006
10007 var view = cm.display.view;
10008 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10009 {node: view[0].measure.map[2], offset: 0};
10010 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10011 if (!end) {
10012 var measure = view[view.length - 1].measure;
10013 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10014 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10015 }
10016
10017 if (!start || !end) {
10018 sel.removeAllRanges();
10019 return
10020 }
10021
10022 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10023 try { rng = range(start.node, start.offset, end.offset, end.node); }
10024 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10025 if (rng) {
10026 if (!gecko && cm.state.focused) {
10027 sel.collapse(start.node, start.offset);
10028 if (!rng.collapsed) {
10029 sel.removeAllRanges();
10030 sel.addRange(rng);
10031 }
10032 } else {
10033 sel.removeAllRanges();
10034 sel.addRange(rng);
10035 }
10036 if (old && sel.anchorNode == null) { sel.addRange(old); }
10037 else if (gecko) { this.startGracePeriod(); }
10038 }
10039 this.rememberSelection();
10040 };
10041
10042 ContentEditableInput.prototype.startGracePeriod = function () {
10043 var this$1 = this;
10044
10045 clearTimeout(this.gracePeriod);
10046 this.gracePeriod = setTimeout(function () {
10047 this$1.gracePeriod = false;
10048 if (this$1.selectionChanged())
10049 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10050 }, 20);
10051 };
10052
10053 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10054 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10055 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10056 };
10057
10058 ContentEditableInput.prototype.rememberSelection = function () {
10059 var sel = window.getSelection();
10060 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10061 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10062 };
10063
10064 ContentEditableInput.prototype.selectionInEditor = function () {
10065 var sel = window.getSelection();
10066 if (!sel.rangeCount) { return false }
10067 var node = sel.getRangeAt(0).commonAncestorContainer;
10068 return contains(this.div, node)
10069 };
10070
10071 ContentEditableInput.prototype.focus = function () {
10072 if (this.cm.options.readOnly != "nocursor") {
10073 if (!this.selectionInEditor())
10074 { this.showSelection(this.prepareSelection(), true); }
10075 this.div.focus();
10076 }
10077 };
10078 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10079 ContentEditableInput.prototype.getField = function () { return this.div };
10080
10081 ContentEditableInput.prototype.supportsTouch = function () { return true };
10082
10083 ContentEditableInput.prototype.receivedFocus = function () {
10084 var input = this;
10085 if (this.selectionInEditor())
10086 { this.pollSelection(); }
10087 else
10088 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10089
10090 function poll() {
10091 if (input.cm.state.focused) {
10092 input.pollSelection();
10093 input.polling.set(input.cm.options.pollInterval, poll);
10094 }
10095 }
10096 this.polling.set(this.cm.options.pollInterval, poll);
10097 };
10098
10099 ContentEditableInput.prototype.selectionChanged = function () {
10100 var sel = window.getSelection();
10101 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10102 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10103 };
10104
10105 ContentEditableInput.prototype.pollSelection = function () {
10106 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10107 var sel = window.getSelection(), cm = this.cm;
10108 // On Android Chrome (version 56, at least), backspacing into an
10109 // uneditable block element will put the cursor in that element,
10110 // and then, because it's not editable, hide the virtual keyboard.
10111 // Because Android doesn't allow us to actually detect backspace
10112 // presses in a sane way, this code checks for when that happens
10113 // and simulates a backspace press in this case.
10114 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
10115 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10116 this.blur();
10117 this.focus();
10118 return
10119 }
10120 if (this.composing) { return }
10121 this.rememberSelection();
10122 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10123 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10124 if (anchor && head) { runInOp(cm, function () {
10125 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10126 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10127 }); }
10128 };
10129
10130 ContentEditableInput.prototype.pollContent = function () {
10131 if (this.readDOMTimeout != null) {
10132 clearTimeout(this.readDOMTimeout);
10133 this.readDOMTimeout = null;
10134 }
10135
10136 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10137 var from = sel.from(), to = sel.to();
10138 if (from.ch == 0 && from.line > cm.firstLine())
10139 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10140 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10141 { to = Pos(to.line + 1, 0); }
10142 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10143
10144 var fromIndex, fromLine, fromNode;
10145 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10146 fromLine = lineNo(display.view[0].line);
10147 fromNode = display.view[0].node;
10148 } else {
10149 fromLine = lineNo(display.view[fromIndex].line);
10150 fromNode = display.view[fromIndex - 1].node.nextSibling;
10151 }
10152 var toIndex = findViewIndex(cm, to.line);
10153 var toLine, toNode;
10154 if (toIndex == display.view.length - 1) {
10155 toLine = display.viewTo - 1;
10156 toNode = display.lineDiv.lastChild;
10157 } else {
10158 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10159 toNode = display.view[toIndex + 1].node.previousSibling;
10160 }
10161
10162 if (!fromNode) { return false }
10163 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10164 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10165 while (newText.length > 1 && oldText.length > 1) {
10166 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10167 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10168 else { break }
10169 }
10170
10171 var cutFront = 0, cutEnd = 0;
10172 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10173 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10174 { ++cutFront; }
10175 var newBot = lst(newText), oldBot = lst(oldText);
10176 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10177 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10178 while (cutEnd < maxCutEnd &&
10179 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10180 { ++cutEnd; }
10181 // Try to move start of change to start of selection if ambiguous
10182 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10183 while (cutFront && cutFront > from.ch &&
10184 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10185 cutFront--;
10186 cutEnd++;
10187 }
10188 }
10189
10190 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10191 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10192
10193 var chFrom = Pos(fromLine, cutFront);
10194 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10195 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10196 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10197 return true
10198 }
10199 };
10200
10201 ContentEditableInput.prototype.ensurePolled = function () {
10202 this.forceCompositionEnd();
10203 };
10204 ContentEditableInput.prototype.reset = function () {
10205 this.forceCompositionEnd();
10206 };
10207 ContentEditableInput.prototype.forceCompositionEnd = function () {
10208 if (!this.composing) { return }
10209 clearTimeout(this.readDOMTimeout);
10210 this.composing = null;
10211 this.updateFromDOM();
10212 this.div.blur();
10213 this.div.focus();
10214 };
10215 ContentEditableInput.prototype.readFromDOMSoon = function () {
10216 var this$1 = this;
10217
10218 if (this.readDOMTimeout != null) { return }
10219 this.readDOMTimeout = setTimeout(function () {
10220 this$1.readDOMTimeout = null;
10221 if (this$1.composing) {
10222 if (this$1.composing.done) { this$1.composing = null; }
10223 else { return }
10224 }
10225 this$1.updateFromDOM();
10226 }, 80);
10227 };
10228
10229 ContentEditableInput.prototype.updateFromDOM = function () {
10230 var this$1 = this;
10231
10232 if (this.cm.isReadOnly() || !this.pollContent())
10233 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10234 };
10235
10236 ContentEditableInput.prototype.setUneditable = function (node) {
10237 node.contentEditable = "false";
10238 };
10239
10240 ContentEditableInput.prototype.onKeyPress = function (e) {
10241 if (e.charCode == 0) { return }
10242 e.preventDefault();
10243 if (!this.cm.isReadOnly())
10244 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10245 };
10246
10247 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10248 this.div.contentEditable = String(val != "nocursor");
10249 };
10250
10251 ContentEditableInput.prototype.onContextMenu = function () {};
10252 ContentEditableInput.prototype.resetPosition = function () {};
10253
10254 ContentEditableInput.prototype.needsContentAttribute = true;
10255
10256 function posToDOM(cm, pos) {
10257 var view = findViewForLine(cm, pos.line);
10258 if (!view || view.hidden) { return null }
10259 var line = getLine(cm.doc, pos.line);
10260 var info = mapFromLineView(view, line, pos.line);
10261
10262 var order = getOrder(line, cm.doc.direction), side = "left";
10263 if (order) {
10264 var partPos = getBidiPartAt(order, pos.ch);
10265 side = partPos % 2 ? "right" : "left";
10266 }
10267 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10268 result.offset = result.collapse == "right" ? result.end : result.start;
10269 return result
10270 }
10271
10272 function isInGutter(node) {
10273 for (var scan = node; scan; scan = scan.parentNode)
10274 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10275 return false
10276 }
10277
10278 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10279
10280 function domTextBetween(cm, from, to, fromLine, toLine) {
10281 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
10282 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10283 function close() {
10284 if (closing) {
10285 text += lineSep;
10286 closing = false;
10287 }
10288 }
10289 function addText(str) {
10290 if (str) {
10291 close();
10292 text += str;
10293 }
10294 }
10295 function walk(node) {
10296 if (node.nodeType == 1) {
10297 var cmText = node.getAttribute("cm-text");
10298 if (cmText != null) {
10299 addText(cmText || node.textContent.replace(/\u200b/g, ""));
10300 return
10301 }
10302 var markerID = node.getAttribute("cm-marker"), range$$1;
10303 if (markerID) {
10304 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10305 if (found.length && (range$$1 = found[0].find(0)))
10306 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10307 return
10308 }
10309 if (node.getAttribute("contenteditable") == "false") { return }
10310 var isBlock = /^(pre|div|p)$/i.test(node.nodeName);
10311 if (isBlock) { close(); }
10312 for (var i = 0; i < node.childNodes.length; i++)
10313 { walk(node.childNodes[i]); }
10314 if (isBlock) { closing = true; }
10315 } else if (node.nodeType == 3) {
10316 addText(node.nodeValue);
10317 }
10318 }
10319 for (;;) {
10320 walk(from);
10321 if (from == to) { break }
10322 from = from.nextSibling;
10323 }
10324 return text
10325 }
10326
10327 function domToPos(cm, node, offset) {
10328 var lineNode;
10329 if (node == cm.display.lineDiv) {
10330 lineNode = cm.display.lineDiv.childNodes[offset];
10331 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10332 node = null; offset = 0;
10333 } else {
10334 for (lineNode = node;; lineNode = lineNode.parentNode) {
10335 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10336 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10337 }
10338 }
10339 for (var i = 0; i < cm.display.view.length; i++) {
10340 var lineView = cm.display.view[i];
10341 if (lineView.node == lineNode)
10342 { return locateNodeInLineView(lineView, node, offset) }
10343 }
10344 }
10345
10346 function locateNodeInLineView(lineView, node, offset) {
10347 var wrapper = lineView.text.firstChild, bad = false;
10348 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10349 if (node == wrapper) {
10350 bad = true;
10351 node = wrapper.childNodes[offset];
10352 offset = 0;
10353 if (!node) {
10354 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10355 return badPos(Pos(lineNo(line), line.text.length), bad)
10356 }
10357 }
10358
10359 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10360 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10361 textNode = node.firstChild;
10362 if (offset) { offset = textNode.nodeValue.length; }
10363 }
10364 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10365 var measure = lineView.measure, maps = measure.maps;
10366
10367 function find(textNode, topNode, offset) {
10368 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10369 var map$$1 = i < 0 ? measure.map : maps[i];
10370 for (var j = 0; j < map$$1.length; j += 3) {
10371 var curNode = map$$1[j + 2];
10372 if (curNode == textNode || curNode == topNode) {
10373 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10374 var ch = map$$1[j] + offset;
10375 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10376 return Pos(line, ch)
10377 }
10378 }
10379 }
10380 }
10381 var found = find(textNode, topNode, offset);
10382 if (found) { return badPos(found, bad) }
10383
10384 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10385 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10386 found = find(after, after.firstChild, 0);
10387 if (found)
10388 { return badPos(Pos(found.line, found.ch - dist), bad) }
10389 else
10390 { dist += after.textContent.length; }
10391 }
10392 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10393 found = find(before, before.firstChild, -1);
10394 if (found)
10395 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10396 else
10397 { dist$1 += before.textContent.length; }
10398 }
10399 }
10400
10401 // TEXTAREA INPUT STYLE
10402
10403 var TextareaInput = function(cm) {
10404 this.cm = cm;
10405 // See input.poll and input.reset
10406 this.prevInput = "";
10407
10408 // Flag that indicates whether we expect input to appear real soon
10409 // now (after some event like 'keypress' or 'input') and are
10410 // polling intensively.
10411 this.pollingFast = false;
10412 // Self-resetting timeout for the poller
10413 this.polling = new Delayed();
10414 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10415 this.hasSelection = false;
10416 this.composing = null;
10417 };
10418
10419 TextareaInput.prototype.init = function (display) {
10420 var this$1 = this;
10421
10422 var input = this, cm = this.cm;
10423
10424 // Wraps and hides input textarea
10425 var div = this.wrapper = hiddenTextarea();
10426 // The semihidden textarea that is focused when the editor is
10427 // focused, and receives input.
10428 var te = this.textarea = div.firstChild;
10429 display.wrapper.insertBefore(div, display.wrapper.firstChild);
10430
10431 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10432 if (ios) { te.style.width = "0px"; }
10433
10434 on(te, "input", function () {
10435 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10436 input.poll();
10437 });
10438
10439 on(te, "paste", function (e) {
10440 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10441
10442 cm.state.pasteIncoming = true;
10443 input.fastPoll();
10444 });
10445
10446 function prepareCopyCut(e) {
10447 if (signalDOMEvent(cm, e)) { return }
10448 if (cm.somethingSelected()) {
10449 setLastCopied({lineWise: false, text: cm.getSelections()});
10450 } else if (!cm.options.lineWiseCopyCut) {
10451 return
10452 } else {
10453 var ranges = copyableRanges(cm);
10454 setLastCopied({lineWise: true, text: ranges.text});
10455 if (e.type == "cut") {
10456 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10457 } else {
10458 input.prevInput = "";
10459 te.value = ranges.text.join("\n");
10460 selectInput(te);
10461 }
10462 }
10463 if (e.type == "cut") { cm.state.cutIncoming = true; }
10464 }
10465 on(te, "cut", prepareCopyCut);
10466 on(te, "copy", prepareCopyCut);
10467
10468 on(display.scroller, "paste", function (e) {
10469 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10470 cm.state.pasteIncoming = true;
10471 input.focus();
10472 });
10473
10474 // Prevent normal selection in the editor (we handle our own)
10475 on(display.lineSpace, "selectstart", function (e) {
10476 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10477 });
10478
10479 on(te, "compositionstart", function () {
10480 var start = cm.getCursor("from");
10481 if (input.composing) { input.composing.range.clear(); }
10482 input.composing = {
10483 start: start,
10484 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10485 };
10486 });
10487 on(te, "compositionend", function () {
10488 if (input.composing) {
10489 input.poll();
10490 input.composing.range.clear();
10491 input.composing = null;
10492 }
10493 });
10494 };
10495
10496 TextareaInput.prototype.prepareSelection = function () {
10497 // Redraw the selection and/or cursor
10498 var cm = this.cm, display = cm.display, doc = cm.doc;
10499 var result = prepareSelection(cm);
10500
10501 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10502 if (cm.options.moveInputWithCursor) {
10503 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10504 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10505 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10506 headPos.top + lineOff.top - wrapOff.top));
10507 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10508 headPos.left + lineOff.left - wrapOff.left));
10509 }
10510
10511 return result
10512 };
10513
10514 TextareaInput.prototype.showSelection = function (drawn) {
10515 var cm = this.cm, display = cm.display;
10516 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10517 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10518 if (drawn.teTop != null) {
10519 this.wrapper.style.top = drawn.teTop + "px";
10520 this.wrapper.style.left = drawn.teLeft + "px";
10521 }
10522 };
10523
10524 // Reset the input to correspond to the selection (or to be empty,
10525 // when not typing and nothing is selected)
10526 TextareaInput.prototype.reset = function (typing) {
10527 if (this.contextMenuPending || this.composing) { return }
10528 var cm = this.cm;
10529 if (cm.somethingSelected()) {
10530 this.prevInput = "";
10531 var content = cm.getSelection();
10532 this.textarea.value = content;
10533 if (cm.state.focused) { selectInput(this.textarea); }
10534 if (ie && ie_version >= 9) { this.hasSelection = content; }
10535 } else if (!typing) {
10536 this.prevInput = this.textarea.value = "";
10537 if (ie && ie_version >= 9) { this.hasSelection = null; }
10538 }
10539 };
10540
10541 TextareaInput.prototype.getField = function () { return this.textarea };
10542
10543 TextareaInput.prototype.supportsTouch = function () { return false };
10544
10545 TextareaInput.prototype.focus = function () {
10546 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10547 try { this.textarea.focus(); }
10548 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
10549 }
10550 };
10551
10552 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
10553
10554 TextareaInput.prototype.resetPosition = function () {
10555 this.wrapper.style.top = this.wrapper.style.left = 0;
10556 };
10557
10558 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
10559
10560 // Poll for input changes, using the normal rate of polling. This
10561 // runs as long as the editor is focused.
10562 TextareaInput.prototype.slowPoll = function () {
10563 var this$1 = this;
10564
10565 if (this.pollingFast) { return }
10566 this.polling.set(this.cm.options.pollInterval, function () {
10567 this$1.poll();
10568 if (this$1.cm.state.focused) { this$1.slowPoll(); }
10569 });
10570 };
10571
10572 // When an event has just come in that is likely to add or change
10573 // something in the input textarea, we poll faster, to ensure that
10574 // the change appears on the screen quickly.
10575 TextareaInput.prototype.fastPoll = function () {
10576 var missed = false, input = this;
10577 input.pollingFast = true;
10578 function p() {
10579 var changed = input.poll();
10580 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
10581 else {input.pollingFast = false; input.slowPoll();}
10582 }
10583 input.polling.set(20, p);
10584 };
10585
10586 // Read input from the textarea, and update the document to match.
10587 // When something is selected, it is present in the textarea, and
10588 // selected (unless it is huge, in which case a placeholder is
10589 // used). When nothing is selected, the cursor sits after previously
10590 // seen text (can be empty), which is stored in prevInput (we must
10591 // not reset the textarea when typing, because that breaks IME).
10592 TextareaInput.prototype.poll = function () {
10593 var this$1 = this;
10594
10595 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
10596 // Since this is called a *lot*, try to bail out as cheaply as
10597 // possible when it is clear that nothing happened. hasSelection
10598 // will be the case when there is a lot of text in the textarea,
10599 // in which case reading its value would be expensive.
10600 if (this.contextMenuPending || !cm.state.focused ||
10601 (hasSelection(input) && !prevInput && !this.composing) ||
10602 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
10603 { return false }
10604
10605 var text = input.value;
10606 // If nothing changed, bail.
10607 if (text == prevInput && !cm.somethingSelected()) { return false }
10608 // Work around nonsensical selection resetting in IE9/10, and
10609 // inexplicable appearance of private area unicode characters on
10610 // some key combos in Mac (#2689).
10611 if (ie && ie_version >= 9 && this.hasSelection === text ||
10612 mac && /[\uf700-\uf7ff]/.test(text)) {
10613 cm.display.input.reset();
10614 return false
10615 }
10616
10617 if (cm.doc.sel == cm.display.selForContextMenu) {
10618 var first = text.charCodeAt(0);
10619 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
10620 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
10621 }
10622 // Find the part of the input that is actually new
10623 var same = 0, l = Math.min(prevInput.length, text.length);
10624 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
10625
10626 runInOp(cm, function () {
10627 applyTextInput(cm, text.slice(same), prevInput.length - same,
10628 null, this$1.composing ? "*compose" : null);
10629
10630 // Don't leave long text in the textarea, since it makes further polling slow
10631 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
10632 else { this$1.prevInput = text; }
10633
10634 if (this$1.composing) {
10635 this$1.composing.range.clear();
10636 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
10637 {className: "CodeMirror-composing"});
10638 }
10639 });
10640 return true
10641 };
10642
10643 TextareaInput.prototype.ensurePolled = function () {
10644 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
10645 };
10646
10647 TextareaInput.prototype.onKeyPress = function () {
10648 if (ie && ie_version >= 9) { this.hasSelection = null; }
10649 this.fastPoll();
10650 };
10651
10652 TextareaInput.prototype.onContextMenu = function (e) {
10653 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
10654 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
10655 if (!pos || presto) { return } // Opera is difficult.
10656
10657 // Reset the current text selection only if the click is done outside of the selection
10658 // and 'resetSelectionOnContextMenu' option is true.
10659 var reset = cm.options.resetSelectionOnContextMenu;
10660 if (reset && cm.doc.sel.contains(pos) == -1)
10661 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
10662
10663 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
10664 input.wrapper.style.cssText = "position: absolute";
10665 var wrapperBox = input.wrapper.getBoundingClientRect();
10666 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);";
10667 var oldScrollY;
10668 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
10669 display.input.focus();
10670 if (webkit) { window.scrollTo(null, oldScrollY); }
10671 display.input.reset();
10672 // Adds "Select all" to context menu in FF
10673 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
10674 input.contextMenuPending = true;
10675 display.selForContextMenu = cm.doc.sel;
10676 clearTimeout(display.detectingSelectAll);
10677
10678 // Select-all will be greyed out if there's nothing to select, so
10679 // this adds a zero-width space so that we can later check whether
10680 // it got selected.
10681 function prepareSelectAllHack() {
10682 if (te.selectionStart != null) {
10683 var selected = cm.somethingSelected();
10684 var extval = "\u200b" + (selected ? te.value : "");
10685 te.value = "\u21da"; // Used to catch context-menu undo
10686 te.value = extval;
10687 input.prevInput = selected ? "" : "\u200b";
10688 te.selectionStart = 1; te.selectionEnd = extval.length;
10689 // Re-set this, in case some other handler touched the
10690 // selection in the meantime.
10691 display.selForContextMenu = cm.doc.sel;
10692 }
10693 }
10694 function rehide() {
10695 input.contextMenuPending = false;
10696 input.wrapper.style.cssText = oldWrapperCSS;
10697 te.style.cssText = oldCSS;
10698 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
10699
10700 // Try to detect the user choosing select-all
10701 if (te.selectionStart != null) {
10702 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
10703 var i = 0, poll = function () {
10704 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
10705 te.selectionEnd > 0 && input.prevInput == "\u200b") {
10706 operation(cm, selectAll)(cm);
10707 } else if (i++ < 10) {
10708 display.detectingSelectAll = setTimeout(poll, 500);
10709 } else {
10710 display.selForContextMenu = null;
10711 display.input.reset();
10712 }
10713 };
10714 display.detectingSelectAll = setTimeout(poll, 200);
10715 }
10716 }
10717
10718 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
10719 if (captureRightClick) {
10720 e_stop(e);
10721 var mouseup = function () {
10722 off(window, "mouseup", mouseup);
10723 setTimeout(rehide, 20);
10724 };
10725 on(window, "mouseup", mouseup);
10726 } else {
10727 setTimeout(rehide, 50);
10728 }
10729 };
10730
10731 TextareaInput.prototype.readOnlyChanged = function (val) {
10732 if (!val) { this.reset(); }
10733 this.textarea.disabled = val == "nocursor";
10734 };
10735
10736 TextareaInput.prototype.setUneditable = function () {};
10737
10738 TextareaInput.prototype.needsContentAttribute = false;
10739
10740 function fromTextArea(textarea, options) {
10741 options = options ? copyObj(options) : {};
10742 options.value = textarea.value;
10743 if (!options.tabindex && textarea.tabIndex)
10744 { options.tabindex = textarea.tabIndex; }
10745 if (!options.placeholder && textarea.placeholder)
10746 { options.placeholder = textarea.placeholder; }
10747 // Set autofocus to true if this textarea is focused, or if it has
10748 // autofocus and no other element is focused.
10749 if (options.autofocus == null) {
10750 var hasFocus = activeElt();
10751 options.autofocus = hasFocus == textarea ||
10752 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
10753 }
10754
10755 function save() {textarea.value = cm.getValue();}
10756
10757 var realSubmit;
10758 if (textarea.form) {
10759 on(textarea.form, "submit", save);
10760 // Deplorable hack to make the submit method do the right thing.
10761 if (!options.leaveSubmitMethodAlone) {
10762 var form = textarea.form;
10763 realSubmit = form.submit;
10764 try {
10765 var wrappedSubmit = form.submit = function () {
10766 save();
10767 form.submit = realSubmit;
10768 form.submit();
10769 form.submit = wrappedSubmit;
10770 };
10771 } catch(e) {}
10772 }
10773 }
10774
10775 options.finishInit = function (cm) {
10776 cm.save = save;
10777 cm.getTextArea = function () { return textarea; };
10778 cm.toTextArea = function () {
10779 cm.toTextArea = isNaN; // Prevent this from being ran twice
10780 save();
10781 textarea.parentNode.removeChild(cm.getWrapperElement());
10782 textarea.style.display = "";
10783 if (textarea.form) {
10784 off(textarea.form, "submit", save);
10785 if (typeof textarea.form.submit == "function")
10786 { textarea.form.submit = realSubmit; }
10787 }
10788 };
10789 };
10790
10791 textarea.style.display = "none";
10792 var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
10793 options);
10794 return cm
10795 }
10796
10797 function addLegacyProps(CodeMirror) {
10798 CodeMirror.off = off;
10799 CodeMirror.on = on;
10800 CodeMirror.wheelEventPixels = wheelEventPixels;
10801 CodeMirror.Doc = Doc;
10802 CodeMirror.splitLines = splitLinesAuto;
10803 CodeMirror.countColumn = countColumn;
10804 CodeMirror.findColumn = findColumn;
10805 CodeMirror.isWordChar = isWordCharBasic;
10806 CodeMirror.Pass = Pass;
10807 CodeMirror.signal = signal;
10808 CodeMirror.Line = Line;
10809 CodeMirror.changeEnd = changeEnd;
10810 CodeMirror.scrollbarModel = scrollbarModel;
10811 CodeMirror.Pos = Pos;
10812 CodeMirror.cmpPos = cmp;
10813 CodeMirror.modes = modes;
10814 CodeMirror.mimeModes = mimeModes;
10815 CodeMirror.resolveMode = resolveMode;
10816 CodeMirror.getMode = getMode;
10817 CodeMirror.modeExtensions = modeExtensions;
10818 CodeMirror.extendMode = extendMode;
10819 CodeMirror.copyState = copyState;
10820 CodeMirror.startState = startState;
10821 CodeMirror.innerMode = innerMode;
10822 CodeMirror.commands = commands;
10823 CodeMirror.keyMap = keyMap;
10824 CodeMirror.keyName = keyName;
10825 CodeMirror.isModifierKey = isModifierKey;
10826 CodeMirror.lookupKey = lookupKey;
10827 CodeMirror.normalizeKeyMap = normalizeKeyMap;
10828 CodeMirror.StringStream = StringStream;
10829 CodeMirror.SharedTextMarker = SharedTextMarker;
10830 CodeMirror.TextMarker = TextMarker;
10831 CodeMirror.LineWidget = LineWidget;
10832 CodeMirror.e_preventDefault = e_preventDefault;
10833 CodeMirror.e_stopPropagation = e_stopPropagation;
10834 CodeMirror.e_stop = e_stop;
10835 CodeMirror.addClass = addClass;
10836 CodeMirror.contains = contains;
10837 CodeMirror.rmClass = rmClass;
10838 CodeMirror.keyNames = keyNames;
10839 }
10840
10841 // EDITOR CONSTRUCTOR
10842
10843 defineOptions(CodeMirror$1);
10844
10845 addEditorMethods(CodeMirror$1);
10846
10847 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
10848 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
10849 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
10850 { CodeMirror$1.prototype[prop] = (function(method) {
10851 return function() {return method.apply(this.doc, arguments)}
10852 })(Doc.prototype[prop]); } }
10853
10854 eventMixin(Doc);
10855
10856 // INPUT HANDLING
10857
10858 CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
10859
10860 // MODE DEFINITION AND QUERYING
10861
10862 // Extra arguments are stored as the mode's dependencies, which is
10863 // used by (legacy) mechanisms like loadmode.js to automatically
10864 // load a mode. (Preferred mechanism is the require/define calls.)
10865 CodeMirror$1.defineMode = function(name/*, mode, …*/) {
10866 if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; }
10867 defineMode.apply(this, arguments);
10868 };
10869
10870 CodeMirror$1.defineMIME = defineMIME;
10871
10872 // Minimal default mode.
10873 CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
10874 CodeMirror$1.defineMIME("text/plain", "null");
10875
10876 // EXTENSIONS
10877
10878 CodeMirror$1.defineExtension = function (name, func) {
10879 CodeMirror$1.prototype[name] = func;
10880 };
10881 CodeMirror$1.defineDocExtension = function (name, func) {
10882 Doc.prototype[name] = func;
10883 };
10884
10885 CodeMirror$1.fromTextArea = fromTextArea;
10886
10887 addLegacyProps(CodeMirror$1);
10888
10889 CodeMirror$1.version = "5.29.0";
10890
10891 return CodeMirror$1;
10892
10893 })));
10894
10895 },{}],13:[function(require,module,exports){
10896 // CodeMirror, copyright (c) by Marijn Haverbeke and others
10897 // Distributed under an MIT license: http://codemirror.net/LICENSE
10898
10899 (function(mod) {
10900 if (typeof exports == "object" && typeof module == "object") // CommonJS
10901 mod(require("../../lib/codemirror"));
10902 else if (typeof define == "function" && define.amd) // AMD
10903 define(["../../lib/codemirror"], mod);
10904 else // Plain browser env
10905 mod(CodeMirror);
10906 })(function(CodeMirror) {
10907 "use strict";
10908
10909 CodeMirror.defineMode("css", function(config, parserConfig) {
10910 var inline = parserConfig.inline
10911 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
10912
10913 var indentUnit = config.indentUnit,
10914 tokenHooks = parserConfig.tokenHooks,
10915 documentTypes = parserConfig.documentTypes || {},
10916 mediaTypes = parserConfig.mediaTypes || {},
10917 mediaFeatures = parserConfig.mediaFeatures || {},
10918 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
10919 propertyKeywords = parserConfig.propertyKeywords || {},
10920 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
10921 fontProperties = parserConfig.fontProperties || {},
10922 counterDescriptors = parserConfig.counterDescriptors || {},
10923 colorKeywords = parserConfig.colorKeywords || {},
10924 valueKeywords = parserConfig.valueKeywords || {},
10925 allowNested = parserConfig.allowNested,
10926 lineComment = parserConfig.lineComment,
10927 supportsAtComponent = parserConfig.supportsAtComponent === true;
10928
10929 var type, override;
10930 function ret(style, tp) { type = tp; return style; }
10931
10932 // Tokenizers
10933
10934 function tokenBase(stream, state) {
10935 var ch = stream.next();
10936 if (tokenHooks[ch]) {
10937 var result = tokenHooks[ch](stream, state);
10938 if (result !== false) return result;
10939 }
10940 if (ch == "@") {
10941 stream.eatWhile(/[\w\\\-]/);
10942 return ret("def", stream.current());
10943 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
10944 return ret(null, "compare");
10945 } else if (ch == "\"" || ch == "'") {
10946 state.tokenize = tokenString(ch);
10947 return state.tokenize(stream, state);
10948 } else if (ch == "#") {
10949 stream.eatWhile(/[\w\\\-]/);
10950 return ret("atom", "hash");
10951 } else if (ch == "!") {
10952 stream.match(/^\s*\w*/);
10953 return ret("keyword", "important");
10954 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
10955 stream.eatWhile(/[\w.%]/);
10956 return ret("number", "unit");
10957 } else if (ch === "-") {
10958 if (/[\d.]/.test(stream.peek())) {
10959 stream.eatWhile(/[\w.%]/);
10960 return ret("number", "unit");
10961 } else if (stream.match(/^-[\w\\\-]+/)) {
10962 stream.eatWhile(/[\w\\\-]/);
10963 if (stream.match(/^\s*:/, false))
10964 return ret("variable-2", "variable-definition");
10965 return ret("variable-2", "variable");
10966 } else if (stream.match(/^\w+-/)) {
10967 return ret("meta", "meta");
10968 }
10969 } else if (/[,+>*\/]/.test(ch)) {
10970 return ret(null, "select-op");
10971 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
10972 return ret("qualifier", "qualifier");
10973 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
10974 return ret(null, ch);
10975 } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
10976 (ch == "d" && stream.match("omain(")) ||
10977 (ch == "r" && stream.match("egexp("))) {
10978 stream.backUp(1);
10979 state.tokenize = tokenParenthesized;
10980 return ret("property", "word");
10981 } else if (/[\w\\\-]/.test(ch)) {
10982 stream.eatWhile(/[\w\\\-]/);
10983 return ret("property", "word");
10984 } else {
10985 return ret(null, null);
10986 }
10987 }
10988
10989 function tokenString(quote) {
10990 return function(stream, state) {
10991 var escaped = false, ch;
10992 while ((ch = stream.next()) != null) {
10993 if (ch == quote && !escaped) {
10994 if (quote == ")") stream.backUp(1);
10995 break;
10996 }
10997 escaped = !escaped && ch == "\\";
10998 }
10999 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11000 return ret("string", "string");
11001 };
11002 }
11003
11004 function tokenParenthesized(stream, state) {
11005 stream.next(); // Must be '('
11006 if (!stream.match(/\s*[\"\')]/, false))
11007 state.tokenize = tokenString(")");
11008 else
11009 state.tokenize = null;
11010 return ret(null, "(");
11011 }
11012
11013 // Context management
11014
11015 function Context(type, indent, prev) {
11016 this.type = type;
11017 this.indent = indent;
11018 this.prev = prev;
11019 }
11020
11021 function pushContext(state, stream, type, indent) {
11022 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11023 return type;
11024 }
11025
11026 function popContext(state) {
11027 if (state.context.prev)
11028 state.context = state.context.prev;
11029 return state.context.type;
11030 }
11031
11032 function pass(type, stream, state) {
11033 return states[state.context.type](type, stream, state);
11034 }
11035 function popAndPass(type, stream, state, n) {
11036 for (var i = n || 1; i > 0; i--)
11037 state.context = state.context.prev;
11038 return pass(type, stream, state);
11039 }
11040
11041 // Parser
11042
11043 function wordAsValue(stream) {
11044 var word = stream.current().toLowerCase();
11045 if (valueKeywords.hasOwnProperty(word))
11046 override = "atom";
11047 else if (colorKeywords.hasOwnProperty(word))
11048 override = "keyword";
11049 else
11050 override = "variable";
11051 }
11052
11053 var states = {};
11054
11055 states.top = function(type, stream, state) {
11056 if (type == "{") {
11057 return pushContext(state, stream, "block");
11058 } else if (type == "}" && state.context.prev) {
11059 return popContext(state);
11060 } else if (supportsAtComponent && /@component/.test(type)) {
11061 return pushContext(state, stream, "atComponentBlock");
11062 } else if (/^@(-moz-)?document$/.test(type)) {
11063 return pushContext(state, stream, "documentTypes");
11064 } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
11065 return pushContext(state, stream, "atBlock");
11066 } else if (/^@(font-face|counter-style)/.test(type)) {
11067 state.stateArg = type;
11068 return "restricted_atBlock_before";
11069 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
11070 return "keyframes";
11071 } else if (type && type.charAt(0) == "@") {
11072 return pushContext(state, stream, "at");
11073 } else if (type == "hash") {
11074 override = "builtin";
11075 } else if (type == "word") {
11076 override = "tag";
11077 } else if (type == "variable-definition") {
11078 return "maybeprop";
11079 } else if (type == "interpolation") {
11080 return pushContext(state, stream, "interpolation");
11081 } else if (type == ":") {
11082 return "pseudo";
11083 } else if (allowNested && type == "(") {
11084 return pushContext(state, stream, "parens");
11085 }
11086 return state.context.type;
11087 };
11088
11089 states.block = function(type, stream, state) {
11090 if (type == "word") {
11091 var word = stream.current().toLowerCase();
11092 if (propertyKeywords.hasOwnProperty(word)) {
11093 override = "property";
11094 return "maybeprop";
11095 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11096 override = "string-2";
11097 return "maybeprop";
11098 } else if (allowNested) {
11099 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11100 return "block";
11101 } else {
11102 override += " error";
11103 return "maybeprop";
11104 }
11105 } else if (type == "meta") {
11106 return "block";
11107 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11108 override = "error";
11109 return "block";
11110 } else {
11111 return states.top(type, stream, state);
11112 }
11113 };
11114
11115 states.maybeprop = function(type, stream, state) {
11116 if (type == ":") return pushContext(state, stream, "prop");
11117 return pass(type, stream, state);
11118 };
11119
11120 states.prop = function(type, stream, state) {
11121 if (type == ";") return popContext(state);
11122 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11123 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11124 if (type == "(") return pushContext(state, stream, "parens");
11125
11126 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11127 override += " error";
11128 } else if (type == "word") {
11129 wordAsValue(stream);
11130 } else if (type == "interpolation") {
11131 return pushContext(state, stream, "interpolation");
11132 }
11133 return "prop";
11134 };
11135
11136 states.propBlock = function(type, _stream, state) {
11137 if (type == "}") return popContext(state);
11138 if (type == "word") { override = "property"; return "maybeprop"; }
11139 return state.context.type;
11140 };
11141
11142 states.parens = function(type, stream, state) {
11143 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11144 if (type == ")") return popContext(state);
11145 if (type == "(") return pushContext(state, stream, "parens");
11146 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11147 if (type == "word") wordAsValue(stream);
11148 return "parens";
11149 };
11150
11151 states.pseudo = function(type, stream, state) {
11152 if (type == "meta") return "pseudo";
11153
11154 if (type == "word") {
11155 override = "variable-3";
11156 return state.context.type;
11157 }
11158 return pass(type, stream, state);
11159 };
11160
11161 states.documentTypes = function(type, stream, state) {
11162 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11163 override = "tag";
11164 return state.context.type;
11165 } else {
11166 return states.atBlock(type, stream, state);
11167 }
11168 };
11169
11170 states.atBlock = function(type, stream, state) {
11171 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11172 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11173 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11174
11175 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11176
11177 if (type == "word") {
11178 var word = stream.current().toLowerCase();
11179 if (word == "only" || word == "not" || word == "and" || word == "or")
11180 override = "keyword";
11181 else if (mediaTypes.hasOwnProperty(word))
11182 override = "attribute";
11183 else if (mediaFeatures.hasOwnProperty(word))
11184 override = "property";
11185 else if (mediaValueKeywords.hasOwnProperty(word))
11186 override = "keyword";
11187 else if (propertyKeywords.hasOwnProperty(word))
11188 override = "property";
11189 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11190 override = "string-2";
11191 else if (valueKeywords.hasOwnProperty(word))
11192 override = "atom";
11193 else if (colorKeywords.hasOwnProperty(word))
11194 override = "keyword";
11195 else
11196 override = "error";
11197 }
11198 return state.context.type;
11199 };
11200
11201 states.atComponentBlock = function(type, stream, state) {
11202 if (type == "}")
11203 return popAndPass(type, stream, state);
11204 if (type == "{")
11205 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11206 if (type == "word")
11207 override = "error";
11208 return state.context.type;
11209 };
11210
11211 states.atBlock_parens = function(type, stream, state) {
11212 if (type == ")") return popContext(state);
11213 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11214 return states.atBlock(type, stream, state);
11215 };
11216
11217 states.restricted_atBlock_before = function(type, stream, state) {
11218 if (type == "{")
11219 return pushContext(state, stream, "restricted_atBlock");
11220 if (type == "word" && state.stateArg == "@counter-style") {
11221 override = "variable";
11222 return "restricted_atBlock_before";
11223 }
11224 return pass(type, stream, state);
11225 };
11226
11227 states.restricted_atBlock = function(type, stream, state) {
11228 if (type == "}") {
11229 state.stateArg = null;
11230 return popContext(state);
11231 }
11232 if (type == "word") {
11233 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11234 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11235 override = "error";
11236 else
11237 override = "property";
11238 return "maybeprop";
11239 }
11240 return "restricted_atBlock";
11241 };
11242
11243 states.keyframes = function(type, stream, state) {
11244 if (type == "word") { override = "variable"; return "keyframes"; }
11245 if (type == "{") return pushContext(state, stream, "top");
11246 return pass(type, stream, state);
11247 };
11248
11249 states.at = function(type, stream, state) {
11250 if (type == ";") return popContext(state);
11251 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11252 if (type == "word") override = "tag";
11253 else if (type == "hash") override = "builtin";
11254 return "at";
11255 };
11256
11257 states.interpolation = function(type, stream, state) {
11258 if (type == "}") return popContext(state);
11259 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11260 if (type == "word") override = "variable";
11261 else if (type != "variable" && type != "(" && type != ")") override = "error";
11262 return "interpolation";
11263 };
11264
11265 return {
11266 startState: function(base) {
11267 return {tokenize: null,
11268 state: inline ? "block" : "top",
11269 stateArg: null,
11270 context: new Context(inline ? "block" : "top", base || 0, null)};
11271 },
11272
11273 token: function(stream, state) {
11274 if (!state.tokenize && stream.eatSpace()) return null;
11275 var style = (state.tokenize || tokenBase)(stream, state);
11276 if (style && typeof style == "object") {
11277 type = style[1];
11278 style = style[0];
11279 }
11280 override = style;
11281 if (type != "comment")
11282 state.state = states[state.state](type, stream, state);
11283 return override;
11284 },
11285
11286 indent: function(state, textAfter) {
11287 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11288 var indent = cx.indent;
11289 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11290 if (cx.prev) {
11291 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11292 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11293 // Resume indentation from parent context.
11294 cx = cx.prev;
11295 indent = cx.indent;
11296 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11297 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11298 // Dedent relative to current context.
11299 indent = Math.max(0, cx.indent - indentUnit);
11300 }
11301 }
11302 return indent;
11303 },
11304
11305 electricChars: "}",
11306 blockCommentStart: "/*",
11307 blockCommentEnd: "*/",
11308 lineComment: lineComment,
11309 fold: "brace"
11310 };
11311 });
11312
11313 function keySet(array) {
11314 var keys = {};
11315 for (var i = 0; i < array.length; ++i) {
11316 keys[array[i].toLowerCase()] = true;
11317 }
11318 return keys;
11319 }
11320
11321 var documentTypes_ = [
11322 "domain", "regexp", "url", "url-prefix"
11323 ], documentTypes = keySet(documentTypes_);
11324
11325 var mediaTypes_ = [
11326 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11327 "tty", "tv", "embossed"
11328 ], mediaTypes = keySet(mediaTypes_);
11329
11330 var mediaFeatures_ = [
11331 "width", "min-width", "max-width", "height", "min-height", "max-height",
11332 "device-width", "min-device-width", "max-device-width", "device-height",
11333 "min-device-height", "max-device-height", "aspect-ratio",
11334 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11335 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11336 "max-color", "color-index", "min-color-index", "max-color-index",
11337 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11338 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11339 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11340 "pointer", "any-pointer", "hover", "any-hover"
11341 ], mediaFeatures = keySet(mediaFeatures_);
11342
11343 var mediaValueKeywords_ = [
11344 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11345 "interlace", "progressive"
11346 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11347
11348 var propertyKeywords_ = [
11349 "align-content", "align-items", "align-self", "alignment-adjust",
11350 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11351 "animation-direction", "animation-duration", "animation-fill-mode",
11352 "animation-iteration-count", "animation-name", "animation-play-state",
11353 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11354 "background", "background-attachment", "background-blend-mode", "background-clip",
11355 "background-color", "background-image", "background-origin", "background-position",
11356 "background-repeat", "background-size", "baseline-shift", "binding",
11357 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11358 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11359 "border-bottom-left-radius", "border-bottom-right-radius",
11360 "border-bottom-style", "border-bottom-width", "border-collapse",
11361 "border-color", "border-image", "border-image-outset",
11362 "border-image-repeat", "border-image-slice", "border-image-source",
11363 "border-image-width", "border-left", "border-left-color",
11364 "border-left-style", "border-left-width", "border-radius", "border-right",
11365 "border-right-color", "border-right-style", "border-right-width",
11366 "border-spacing", "border-style", "border-top", "border-top-color",
11367 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11368 "border-top-width", "border-width", "bottom", "box-decoration-break",
11369 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11370 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11371 "column-fill", "column-gap", "column-rule", "column-rule-color",
11372 "column-rule-style", "column-rule-width", "column-span", "column-width",
11373 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11374 "cue-after", "cue-before", "cursor", "direction", "display",
11375 "dominant-baseline", "drop-initial-after-adjust",
11376 "drop-initial-after-align", "drop-initial-before-adjust",
11377 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11378 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11379 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11380 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11381 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11382 "font-stretch", "font-style", "font-synthesis", "font-variant",
11383 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11384 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11385 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11386 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11387 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11388 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11389 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11390 "icon", "image-orientation", "image-rendering", "image-resolution",
11391 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11392 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11393 "line-stacking-shift", "line-stacking-strategy", "list-style",
11394 "list-style-image", "list-style-position", "list-style-type", "margin",
11395 "margin-bottom", "margin-left", "margin-right", "margin-top",
11396 "marks", "marquee-direction", "marquee-loop",
11397 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11398 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
11399 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11400 "opacity", "order", "orphans", "outline",
11401 "outline-color", "outline-offset", "outline-style", "outline-width",
11402 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11403 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11404 "page", "page-break-after", "page-break-before", "page-break-inside",
11405 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11406 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11407 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11408 "region-break-before", "region-break-inside", "region-fragment",
11409 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11410 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11411 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11412 "shape-outside", "size", "speak", "speak-as", "speak-header",
11413 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11414 "tab-size", "table-layout", "target", "target-name", "target-new",
11415 "target-position", "text-align", "text-align-last", "text-decoration",
11416 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11417 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11418 "text-emphasis-position", "text-emphasis-style", "text-height",
11419 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11420 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11421 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11422 "transition", "transition-delay", "transition-duration",
11423 "transition-property", "transition-timing-function", "unicode-bidi",
11424 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11425 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11426 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11427 "word-spacing", "word-wrap", "z-index",
11428 // SVG-specific
11429 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11430 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11431 "color-interpolation", "color-interpolation-filters",
11432 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11433 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11434 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11435 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11436 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11437 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11438 ], propertyKeywords = keySet(propertyKeywords_);
11439
11440 var nonStandardPropertyKeywords_ = [
11441 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11442 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11443 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11444 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11445 "searchfield-results-decoration", "zoom"
11446 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11447
11448 var fontProperties_ = [
11449 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11450 "font-stretch", "font-weight", "font-style"
11451 ], fontProperties = keySet(fontProperties_);
11452
11453 var counterDescriptors_ = [
11454 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11455 "speak-as", "suffix", "symbols", "system"
11456 ], counterDescriptors = keySet(counterDescriptors_);
11457
11458 var colorKeywords_ = [
11459 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11460 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11461 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11462 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11463 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11464 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11465 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11466 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11467 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11468 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11469 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11470 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11471 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11472 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11473 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11474 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11475 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11476 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11477 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11478 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11479 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11480 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11481 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11482 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11483 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11484 "whitesmoke", "yellow", "yellowgreen"
11485 ], colorKeywords = keySet(colorKeywords_);
11486
11487 var valueKeywords_ = [
11488 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11489 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11490 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11491 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11492 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11493 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11494 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11495 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11496 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11497 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11498 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11499 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11500 "compact", "condensed", "contain", "content", "contents",
11501 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11502 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11503 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11504 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11505 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11506 "dot-dash", "dot-dot-dash",
11507 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11508 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11509 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11510 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11511 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11512 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11513 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11514 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11515 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11516 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11517 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11518 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11519 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11520 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11521 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11522 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11523 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11524 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11525 "katakana", "katakana-iroha", "keep-all", "khmer",
11526 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11527 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11528 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11529 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11530 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11531 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11532 "media-controls-background", "media-current-time-display",
11533 "media-fullscreen-button", "media-mute-button", "media-play-button",
11534 "media-return-to-realtime-button", "media-rewind-button",
11535 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11536 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11537 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11538 "menu", "menulist", "menulist-button", "menulist-text",
11539 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11540 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11541 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11542 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11543 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11544 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11545 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11546 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11547 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11548 "progress", "push-button", "radial-gradient", "radio", "read-only",
11549 "read-write", "read-write-plaintext-only", "rectangle", "region",
11550 "relative", "repeat", "repeating-linear-gradient",
11551 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
11552 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
11553 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
11554 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
11555 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
11556 "searchfield-cancel-button", "searchfield-decoration",
11557 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
11558 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
11559 "simp-chinese-formal", "simp-chinese-informal", "single",
11560 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
11561 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
11562 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
11563 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
11564 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
11565 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
11566 "table-caption", "table-cell", "table-column", "table-column-group",
11567 "table-footer-group", "table-header-group", "table-row", "table-row-group",
11568 "tamil",
11569 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
11570 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
11571 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
11572 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
11573 "trad-chinese-formal", "trad-chinese-informal", "transform",
11574 "translate", "translate3d", "translateX", "translateY", "translateZ",
11575 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
11576 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
11577 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
11578 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
11579 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
11580 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
11581 "xx-large", "xx-small"
11582 ], valueKeywords = keySet(valueKeywords_);
11583
11584 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
11585 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
11586 .concat(valueKeywords_);
11587 CodeMirror.registerHelper("hintWords", "css", allWords);
11588
11589 function tokenCComment(stream, state) {
11590 var maybeEnd = false, ch;
11591 while ((ch = stream.next()) != null) {
11592 if (maybeEnd && ch == "/") {
11593 state.tokenize = null;
11594 break;
11595 }
11596 maybeEnd = (ch == "*");
11597 }
11598 return ["comment", "comment"];
11599 }
11600
11601 CodeMirror.defineMIME("text/css", {
11602 documentTypes: documentTypes,
11603 mediaTypes: mediaTypes,
11604 mediaFeatures: mediaFeatures,
11605 mediaValueKeywords: mediaValueKeywords,
11606 propertyKeywords: propertyKeywords,
11607 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11608 fontProperties: fontProperties,
11609 counterDescriptors: counterDescriptors,
11610 colorKeywords: colorKeywords,
11611 valueKeywords: valueKeywords,
11612 tokenHooks: {
11613 "/": function(stream, state) {
11614 if (!stream.eat("*")) return false;
11615 state.tokenize = tokenCComment;
11616 return tokenCComment(stream, state);
11617 }
11618 },
11619 name: "css"
11620 });
11621
11622 CodeMirror.defineMIME("text/x-scss", {
11623 mediaTypes: mediaTypes,
11624 mediaFeatures: mediaFeatures,
11625 mediaValueKeywords: mediaValueKeywords,
11626 propertyKeywords: propertyKeywords,
11627 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11628 colorKeywords: colorKeywords,
11629 valueKeywords: valueKeywords,
11630 fontProperties: fontProperties,
11631 allowNested: true,
11632 lineComment: "//",
11633 tokenHooks: {
11634 "/": function(stream, state) {
11635 if (stream.eat("/")) {
11636 stream.skipToEnd();
11637 return ["comment", "comment"];
11638 } else if (stream.eat("*")) {
11639 state.tokenize = tokenCComment;
11640 return tokenCComment(stream, state);
11641 } else {
11642 return ["operator", "operator"];
11643 }
11644 },
11645 ":": function(stream) {
11646 if (stream.match(/\s*\{/, false))
11647 return [null, null]
11648 return false;
11649 },
11650 "$": function(stream) {
11651 stream.match(/^[\w-]+/);
11652 if (stream.match(/^\s*:/, false))
11653 return ["variable-2", "variable-definition"];
11654 return ["variable-2", "variable"];
11655 },
11656 "#": function(stream) {
11657 if (!stream.eat("{")) return false;
11658 return [null, "interpolation"];
11659 }
11660 },
11661 name: "css",
11662 helperType: "scss"
11663 });
11664
11665 CodeMirror.defineMIME("text/x-less", {
11666 mediaTypes: mediaTypes,
11667 mediaFeatures: mediaFeatures,
11668 mediaValueKeywords: mediaValueKeywords,
11669 propertyKeywords: propertyKeywords,
11670 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11671 colorKeywords: colorKeywords,
11672 valueKeywords: valueKeywords,
11673 fontProperties: fontProperties,
11674 allowNested: true,
11675 lineComment: "//",
11676 tokenHooks: {
11677 "/": function(stream, state) {
11678 if (stream.eat("/")) {
11679 stream.skipToEnd();
11680 return ["comment", "comment"];
11681 } else if (stream.eat("*")) {
11682 state.tokenize = tokenCComment;
11683 return tokenCComment(stream, state);
11684 } else {
11685 return ["operator", "operator"];
11686 }
11687 },
11688 "@": function(stream) {
11689 if (stream.eat("{")) return [null, "interpolation"];
11690 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
11691 stream.eatWhile(/[\w\\\-]/);
11692 if (stream.match(/^\s*:/, false))
11693 return ["variable-2", "variable-definition"];
11694 return ["variable-2", "variable"];
11695 },
11696 "&": function() {
11697 return ["atom", "atom"];
11698 }
11699 },
11700 name: "css",
11701 helperType: "less"
11702 });
11703
11704 CodeMirror.defineMIME("text/x-gss", {
11705 documentTypes: documentTypes,
11706 mediaTypes: mediaTypes,
11707 mediaFeatures: mediaFeatures,
11708 propertyKeywords: propertyKeywords,
11709 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11710 fontProperties: fontProperties,
11711 counterDescriptors: counterDescriptors,
11712 colorKeywords: colorKeywords,
11713 valueKeywords: valueKeywords,
11714 supportsAtComponent: true,
11715 tokenHooks: {
11716 "/": function(stream, state) {
11717 if (!stream.eat("*")) return false;
11718 state.tokenize = tokenCComment;
11719 return tokenCComment(stream, state);
11720 }
11721 },
11722 name: "css",
11723 helperType: "gss"
11724 });
11725
11726 });
11727
11728 },{"../../lib/codemirror":12}],14:[function(require,module,exports){
11729 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11730 // Distributed under an MIT license: http://codemirror.net/LICENSE
11731
11732 (function(mod) {
11733 if (typeof exports == "object" && typeof module == "object") // CommonJS
11734 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
11735 else if (typeof define == "function" && define.amd) // AMD
11736 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
11737 else // Plain browser env
11738 mod(CodeMirror);
11739 })(function(CodeMirror) {
11740 "use strict";
11741
11742 var defaultTags = {
11743 script: [
11744 ["lang", /(javascript|babel)/i, "javascript"],
11745 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
11746 ["type", /./, "text/plain"],
11747 [null, null, "javascript"]
11748 ],
11749 style: [
11750 ["lang", /^css$/i, "css"],
11751 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
11752 ["type", /./, "text/plain"],
11753 [null, null, "css"]
11754 ]
11755 };
11756
11757 function maybeBackup(stream, pat, style) {
11758 var cur = stream.current(), close = cur.search(pat);
11759 if (close > -1) {
11760 stream.backUp(cur.length - close);
11761 } else if (cur.match(/<\/?$/)) {
11762 stream.backUp(cur.length);
11763 if (!stream.match(pat, false)) stream.match(cur);
11764 }
11765 return style;
11766 }
11767
11768 var attrRegexpCache = {};
11769 function getAttrRegexp(attr) {
11770 var regexp = attrRegexpCache[attr];
11771 if (regexp) return regexp;
11772 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
11773 }
11774
11775 function getAttrValue(text, attr) {
11776 var match = text.match(getAttrRegexp(attr))
11777 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
11778 }
11779
11780 function getTagRegexp(tagName, anchored) {
11781 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
11782 }
11783
11784 function addTags(from, to) {
11785 for (var tag in from) {
11786 var dest = to[tag] || (to[tag] = []);
11787 var source = from[tag];
11788 for (var i = source.length - 1; i >= 0; i--)
11789 dest.unshift(source[i])
11790 }
11791 }
11792
11793 function findMatchingMode(tagInfo, tagText) {
11794 for (var i = 0; i < tagInfo.length; i++) {
11795 var spec = tagInfo[i];
11796 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
11797 }
11798 }
11799
11800 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
11801 var htmlMode = CodeMirror.getMode(config, {
11802 name: "xml",
11803 htmlMode: true,
11804 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
11805 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
11806 });
11807
11808 var tags = {};
11809 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
11810 addTags(defaultTags, tags);
11811 if (configTags) addTags(configTags, tags);
11812 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
11813 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
11814
11815 function html(stream, state) {
11816 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
11817 if (tag && !/[<>\s\/]/.test(stream.current()) &&
11818 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
11819 tags.hasOwnProperty(tagName)) {
11820 state.inTag = tagName + " "
11821 } else if (state.inTag && tag && />$/.test(stream.current())) {
11822 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
11823 state.inTag = null
11824 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
11825 var mode = CodeMirror.getMode(config, modeSpec)
11826 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
11827 state.token = function (stream, state) {
11828 if (stream.match(endTagA, false)) {
11829 state.token = html;
11830 state.localState = state.localMode = null;
11831 return null;
11832 }
11833 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
11834 };
11835 state.localMode = mode;
11836 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
11837 } else if (state.inTag) {
11838 state.inTag += stream.current()
11839 if (stream.eol()) state.inTag += " "
11840 }
11841 return style;
11842 };
11843
11844 return {
11845 startState: function () {
11846 var state = CodeMirror.startState(htmlMode);
11847 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
11848 },
11849
11850 copyState: function (state) {
11851 var local;
11852 if (state.localState) {
11853 local = CodeMirror.copyState(state.localMode, state.localState);
11854 }
11855 return {token: state.token, inTag: state.inTag,
11856 localMode: state.localMode, localState: local,
11857 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
11858 },
11859
11860 token: function (stream, state) {
11861 return state.token(stream, state);
11862 },
11863
11864 indent: function (state, textAfter, line) {
11865 if (!state.localMode || /^\s*<\//.test(textAfter))
11866 return htmlMode.indent(state.htmlState, textAfter);
11867 else if (state.localMode.indent)
11868 return state.localMode.indent(state.localState, textAfter, line);
11869 else
11870 return CodeMirror.Pass;
11871 },
11872
11873 innerMode: function (state) {
11874 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
11875 }
11876 };
11877 }, "xml", "javascript", "css");
11878
11879 CodeMirror.defineMIME("text/html", "htmlmixed");
11880 });
11881
11882 },{"../../lib/codemirror":12,"../css/css":13,"../javascript/javascript":15,"../xml/xml":16}],15:[function(require,module,exports){
11883 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11884 // Distributed under an MIT license: http://codemirror.net/LICENSE
11885
11886 (function(mod) {
11887 if (typeof exports == "object" && typeof module == "object") // CommonJS
11888 mod(require("../../lib/codemirror"));
11889 else if (typeof define == "function" && define.amd) // AMD
11890 define(["../../lib/codemirror"], mod);
11891 else // Plain browser env
11892 mod(CodeMirror);
11893 })(function(CodeMirror) {
11894 "use strict";
11895
11896 CodeMirror.defineMode("javascript", function(config, parserConfig) {
11897 var indentUnit = config.indentUnit;
11898 var statementIndent = parserConfig.statementIndent;
11899 var jsonldMode = parserConfig.jsonld;
11900 var jsonMode = parserConfig.json || jsonldMode;
11901 var isTS = parserConfig.typescript;
11902 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
11903
11904 // Tokenizer
11905
11906 var keywords = function(){
11907 function kw(type) {return {type: type, style: "keyword"};}
11908 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
11909 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11910
11911 var jsKeywords = {
11912 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
11913 "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
11914 "var": kw("var"), "const": kw("var"), "let": kw("var"),
11915 "function": kw("function"), "catch": kw("catch"),
11916 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
11917 "in": operator, "typeof": operator, "instanceof": operator,
11918 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
11919 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
11920 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
11921 "await": C
11922 };
11923
11924 // Extend the 'normal' keywords with the TypeScript language extensions
11925 if (isTS) {
11926 var type = {type: "variable", style: "type"};
11927 var tsKeywords = {
11928 // object-like things
11929 "interface": kw("class"),
11930 "implements": C,
11931 "namespace": C,
11932 "module": kw("module"),
11933 "enum": kw("module"),
11934
11935 // scope modifiers
11936 "public": kw("modifier"),
11937 "private": kw("modifier"),
11938 "protected": kw("modifier"),
11939 "abstract": kw("modifier"),
11940 "readonly": kw("modifier"),
11941
11942 // types
11943 "string": type, "number": type, "boolean": type, "any": type
11944 };
11945
11946 for (var attr in tsKeywords) {
11947 jsKeywords[attr] = tsKeywords[attr];
11948 }
11949 }
11950
11951 return jsKeywords;
11952 }();
11953
11954 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
11955 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
11956
11957 function readRegexp(stream) {
11958 var escaped = false, next, inSet = false;
11959 while ((next = stream.next()) != null) {
11960 if (!escaped) {
11961 if (next == "/" && !inSet) return;
11962 if (next == "[") inSet = true;
11963 else if (inSet && next == "]") inSet = false;
11964 }
11965 escaped = !escaped && next == "\\";
11966 }
11967 }
11968
11969 // Used as scratch variables to communicate multiple values without
11970 // consing up tons of objects.
11971 var type, content;
11972 function ret(tp, style, cont) {
11973 type = tp; content = cont;
11974 return style;
11975 }
11976 function tokenBase(stream, state) {
11977 var ch = stream.next();
11978 if (ch == '"' || ch == "'") {
11979 state.tokenize = tokenString(ch);
11980 return state.tokenize(stream, state);
11981 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
11982 return ret("number", "number");
11983 } else if (ch == "." && stream.match("..")) {
11984 return ret("spread", "meta");
11985 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
11986 return ret(ch);
11987 } else if (ch == "=" && stream.eat(">")) {
11988 return ret("=>", "operator");
11989 } else if (ch == "0" && stream.eat(/x/i)) {
11990 stream.eatWhile(/[\da-f]/i);
11991 return ret("number", "number");
11992 } else if (ch == "0" && stream.eat(/o/i)) {
11993 stream.eatWhile(/[0-7]/i);
11994 return ret("number", "number");
11995 } else if (ch == "0" && stream.eat(/b/i)) {
11996 stream.eatWhile(/[01]/i);
11997 return ret("number", "number");
11998 } else if (/\d/.test(ch)) {
11999 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
12000 return ret("number", "number");
12001 } else if (ch == "/") {
12002 if (stream.eat("*")) {
12003 state.tokenize = tokenComment;
12004 return tokenComment(stream, state);
12005 } else if (stream.eat("/")) {
12006 stream.skipToEnd();
12007 return ret("comment", "comment");
12008 } else if (expressionAllowed(stream, state, 1)) {
12009 readRegexp(stream);
12010 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
12011 return ret("regexp", "string-2");
12012 } else {
12013 stream.eatWhile(isOperatorChar);
12014 return ret("operator", "operator", stream.current());
12015 }
12016 } else if (ch == "`") {
12017 state.tokenize = tokenQuasi;
12018 return tokenQuasi(stream, state);
12019 } else if (ch == "#") {
12020 stream.skipToEnd();
12021 return ret("error", "error");
12022 } else if (isOperatorChar.test(ch)) {
12023 if (ch != ">" || !state.lexical || state.lexical.type != ">")
12024 stream.eatWhile(isOperatorChar);
12025 return ret("operator", "operator", stream.current());
12026 } else if (wordRE.test(ch)) {
12027 stream.eatWhile(wordRE);
12028 var word = stream.current()
12029 if (state.lastType != ".") {
12030 if (keywords.propertyIsEnumerable(word)) {
12031 var kw = keywords[word]
12032 return ret(kw.type, kw.style, word)
12033 }
12034 if (word == "async" && stream.match(/^\s*[\(\w]/, false))
12035 return ret("async", "keyword", word)
12036 }
12037 return ret("variable", "variable", word)
12038 }
12039 }
12040
12041 function tokenString(quote) {
12042 return function(stream, state) {
12043 var escaped = false, next;
12044 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12045 state.tokenize = tokenBase;
12046 return ret("jsonld-keyword", "meta");
12047 }
12048 while ((next = stream.next()) != null) {
12049 if (next == quote && !escaped) break;
12050 escaped = !escaped && next == "\\";
12051 }
12052 if (!escaped) state.tokenize = tokenBase;
12053 return ret("string", "string");
12054 };
12055 }
12056
12057 function tokenComment(stream, state) {
12058 var maybeEnd = false, ch;
12059 while (ch = stream.next()) {
12060 if (ch == "/" && maybeEnd) {
12061 state.tokenize = tokenBase;
12062 break;
12063 }
12064 maybeEnd = (ch == "*");
12065 }
12066 return ret("comment", "comment");
12067 }
12068
12069 function tokenQuasi(stream, state) {
12070 var escaped = false, next;
12071 while ((next = stream.next()) != null) {
12072 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12073 state.tokenize = tokenBase;
12074 break;
12075 }
12076 escaped = !escaped && next == "\\";
12077 }
12078 return ret("quasi", "string-2", stream.current());
12079 }
12080
12081 var brackets = "([{}])";
12082 // This is a crude lookahead trick to try and notice that we're
12083 // parsing the argument patterns for a fat-arrow function before we
12084 // actually hit the arrow token. It only works if the arrow is on
12085 // the same line as the arguments and there's no strange noise
12086 // (comments) in between. Fallback is to only notice when we hit the
12087 // arrow, and not declare the arguments as locals for the arrow
12088 // body.
12089 function findFatArrow(stream, state) {
12090 if (state.fatArrowAt) state.fatArrowAt = null;
12091 var arrow = stream.string.indexOf("=>", stream.start);
12092 if (arrow < 0) return;
12093
12094 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12095 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12096 if (m) arrow = m.index
12097 }
12098
12099 var depth = 0, sawSomething = false;
12100 for (var pos = arrow - 1; pos >= 0; --pos) {
12101 var ch = stream.string.charAt(pos);
12102 var bracket = brackets.indexOf(ch);
12103 if (bracket >= 0 && bracket < 3) {
12104 if (!depth) { ++pos; break; }
12105 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12106 } else if (bracket >= 3 && bracket < 6) {
12107 ++depth;
12108 } else if (wordRE.test(ch)) {
12109 sawSomething = true;
12110 } else if (/["'\/]/.test(ch)) {
12111 return;
12112 } else if (sawSomething && !depth) {
12113 ++pos;
12114 break;
12115 }
12116 }
12117 if (sawSomething && !depth) state.fatArrowAt = pos;
12118 }
12119
12120 // Parser
12121
12122 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12123
12124 function JSLexical(indented, column, type, align, prev, info) {
12125 this.indented = indented;
12126 this.column = column;
12127 this.type = type;
12128 this.prev = prev;
12129 this.info = info;
12130 if (align != null) this.align = align;
12131 }
12132
12133 function inScope(state, varname) {
12134 for (var v = state.localVars; v; v = v.next)
12135 if (v.name == varname) return true;
12136 for (var cx = state.context; cx; cx = cx.prev) {
12137 for (var v = cx.vars; v; v = v.next)
12138 if (v.name == varname) return true;
12139 }
12140 }
12141
12142 function parseJS(state, style, type, content, stream) {
12143 var cc = state.cc;
12144 // Communicate our context to the combinators.
12145 // (Less wasteful than consing up a hundred closures on every call.)
12146 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12147
12148 if (!state.lexical.hasOwnProperty("align"))
12149 state.lexical.align = true;
12150
12151 while(true) {
12152 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12153 if (combinator(type, content)) {
12154 while(cc.length && cc[cc.length - 1].lex)
12155 cc.pop()();
12156 if (cx.marked) return cx.marked;
12157 if (type == "variable" && inScope(state, content)) return "variable-2";
12158 return style;
12159 }
12160 }
12161 }
12162
12163 // Combinator utils
12164
12165 var cx = {state: null, column: null, marked: null, cc: null};
12166 function pass() {
12167 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12168 }
12169 function cont() {
12170 pass.apply(null, arguments);
12171 return true;
12172 }
12173 function register(varname) {
12174 function inList(list) {
12175 for (var v = list; v; v = v.next)
12176 if (v.name == varname) return true;
12177 return false;
12178 }
12179 var state = cx.state;
12180 cx.marked = "def";
12181 if (state.context) {
12182 if (inList(state.localVars)) return;
12183 state.localVars = {name: varname, next: state.localVars};
12184 } else {
12185 if (inList(state.globalVars)) return;
12186 if (parserConfig.globalVars)
12187 state.globalVars = {name: varname, next: state.globalVars};
12188 }
12189 }
12190
12191 // Combinators
12192
12193 var defaultVars = {name: "this", next: {name: "arguments"}};
12194 function pushcontext() {
12195 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
12196 cx.state.localVars = defaultVars;
12197 }
12198 function popcontext() {
12199 cx.state.localVars = cx.state.context.vars;
12200 cx.state.context = cx.state.context.prev;
12201 }
12202 function pushlex(type, info) {
12203 var result = function() {
12204 var state = cx.state, indent = state.indented;
12205 if (state.lexical.type == "stat") indent = state.lexical.indented;
12206 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12207 indent = outer.indented;
12208 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12209 };
12210 result.lex = true;
12211 return result;
12212 }
12213 function poplex() {
12214 var state = cx.state;
12215 if (state.lexical.prev) {
12216 if (state.lexical.type == ")")
12217 state.indented = state.lexical.indented;
12218 state.lexical = state.lexical.prev;
12219 }
12220 }
12221 poplex.lex = true;
12222
12223 function expect(wanted) {
12224 function exp(type) {
12225 if (type == wanted) return cont();
12226 else if (wanted == ";") return pass();
12227 else return cont(exp);
12228 };
12229 return exp;
12230 }
12231
12232 function statement(type, value) {
12233 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
12234 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12235 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12236 if (type == "{") return cont(pushlex("}"), block, poplex);
12237 if (type == ";") return cont();
12238 if (type == "if") {
12239 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12240 cx.state.cc.pop()();
12241 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12242 }
12243 if (type == "function") return cont(functiondef);
12244 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12245 if (type == "variable") {
12246 if (isTS && value == "type") {
12247 cx.marked = "keyword"
12248 return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
12249 } if (isTS && value == "declare") {
12250 cx.marked = "keyword"
12251 return cont(statement)
12252 } else {
12253 return cont(pushlex("stat"), maybelabel);
12254 }
12255 }
12256 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
12257 block, poplex, poplex);
12258 if (type == "case") return cont(expression, expect(":"));
12259 if (type == "default") return cont(expect(":"));
12260 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
12261 statement, poplex, popcontext);
12262 if (type == "class") return cont(pushlex("form"), className, poplex);
12263 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12264 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12265 if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12266 if (type == "async") return cont(statement)
12267 if (value == "@") return cont(expression, statement)
12268 return pass(pushlex("stat"), expression, expect(";"), poplex);
12269 }
12270 function expression(type) {
12271 return expressionInner(type, false);
12272 }
12273 function expressionNoComma(type) {
12274 return expressionInner(type, true);
12275 }
12276 function parenExpr(type) {
12277 if (type != "(") return pass()
12278 return cont(pushlex(")"), expression, expect(")"), poplex)
12279 }
12280 function expressionInner(type, noComma) {
12281 if (cx.state.fatArrowAt == cx.stream.start) {
12282 var body = noComma ? arrowBodyNoComma : arrowBody;
12283 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
12284 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12285 }
12286
12287 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12288 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12289 if (type == "function") return cont(functiondef, maybeop);
12290 if (type == "class") return cont(pushlex("form"), classExpression, poplex);
12291 if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
12292 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12293 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12294 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12295 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12296 if (type == "quasi") return pass(quasi, maybeop);
12297 if (type == "new") return cont(maybeTarget(noComma));
12298 return cont();
12299 }
12300 function maybeexpression(type) {
12301 if (type.match(/[;\}\)\],]/)) return pass();
12302 return pass(expression);
12303 }
12304 function maybeexpressionNoComma(type) {
12305 if (type.match(/[;\}\)\],]/)) return pass();
12306 return pass(expressionNoComma);
12307 }
12308
12309 function maybeoperatorComma(type, value) {
12310 if (type == ",") return cont(expression);
12311 return maybeoperatorNoComma(type, value, false);
12312 }
12313 function maybeoperatorNoComma(type, value, noComma) {
12314 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12315 var expr = noComma == false ? expression : expressionNoComma;
12316 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12317 if (type == "operator") {
12318 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12319 if (value == "?") return cont(expression, expect(":"), expr);
12320 return cont(expr);
12321 }
12322 if (type == "quasi") { return pass(quasi, me); }
12323 if (type == ";") return;
12324 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12325 if (type == ".") return cont(property, me);
12326 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12327 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12328 }
12329 function quasi(type, value) {
12330 if (type != "quasi") return pass();
12331 if (value.slice(value.length - 2) != "${") return cont(quasi);
12332 return cont(expression, continueQuasi);
12333 }
12334 function continueQuasi(type) {
12335 if (type == "}") {
12336 cx.marked = "string-2";
12337 cx.state.tokenize = tokenQuasi;
12338 return cont(quasi);
12339 }
12340 }
12341 function arrowBody(type) {
12342 findFatArrow(cx.stream, cx.state);
12343 return pass(type == "{" ? statement : expression);
12344 }
12345 function arrowBodyNoComma(type) {
12346 findFatArrow(cx.stream, cx.state);
12347 return pass(type == "{" ? statement : expressionNoComma);
12348 }
12349 function maybeTarget(noComma) {
12350 return function(type) {
12351 if (type == ".") return cont(noComma ? targetNoComma : target);
12352 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12353 else return pass(noComma ? expressionNoComma : expression);
12354 };
12355 }
12356 function target(_, value) {
12357 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12358 }
12359 function targetNoComma(_, value) {
12360 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12361 }
12362 function maybelabel(type) {
12363 if (type == ":") return cont(poplex, statement);
12364 return pass(maybeoperatorComma, expect(";"), poplex);
12365 }
12366 function property(type) {
12367 if (type == "variable") {cx.marked = "property"; return cont();}
12368 }
12369 function objprop(type, value) {
12370 if (type == "async") {
12371 cx.marked = "property";
12372 return cont(objprop);
12373 } else if (type == "variable" || cx.style == "keyword") {
12374 cx.marked = "property";
12375 if (value == "get" || value == "set") return cont(getterSetter);
12376 return cont(afterprop);
12377 } else if (type == "number" || type == "string") {
12378 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12379 return cont(afterprop);
12380 } else if (type == "jsonld-keyword") {
12381 return cont(afterprop);
12382 } else if (type == "modifier") {
12383 return cont(objprop)
12384 } else if (type == "[") {
12385 return cont(expression, expect("]"), afterprop);
12386 } else if (type == "spread") {
12387 return cont(expression, afterprop);
12388 } else if (type == ":") {
12389 return pass(afterprop)
12390 }
12391 }
12392 function getterSetter(type) {
12393 if (type != "variable") return pass(afterprop);
12394 cx.marked = "property";
12395 return cont(functiondef);
12396 }
12397 function afterprop(type) {
12398 if (type == ":") return cont(expressionNoComma);
12399 if (type == "(") return pass(functiondef);
12400 }
12401 function commasep(what, end, sep) {
12402 function proceed(type, value) {
12403 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12404 var lex = cx.state.lexical;
12405 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12406 return cont(function(type, value) {
12407 if (type == end || value == end) return pass()
12408 return pass(what)
12409 }, proceed);
12410 }
12411 if (type == end || value == end) return cont();
12412 return cont(expect(end));
12413 }
12414 return function(type, value) {
12415 if (type == end || value == end) return cont();
12416 return pass(what, proceed);
12417 };
12418 }
12419 function contCommasep(what, end, info) {
12420 for (var i = 3; i < arguments.length; i++)
12421 cx.cc.push(arguments[i]);
12422 return cont(pushlex(end, info), commasep(what, end), poplex);
12423 }
12424 function block(type) {
12425 if (type == "}") return cont();
12426 return pass(statement, block);
12427 }
12428 function maybetype(type, value) {
12429 if (isTS) {
12430 if (type == ":") return cont(typeexpr);
12431 if (value == "?") return cont(maybetype);
12432 }
12433 }
12434 function typeexpr(type, value) {
12435 if (type == "variable") {
12436 if (value == "keyof") {
12437 cx.marked = "keyword"
12438 return cont(typeexpr)
12439 } else {
12440 cx.marked = "type"
12441 return cont(afterType)
12442 }
12443 }
12444 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12445 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12446 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12447 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
12448 }
12449 function maybeReturnType(type) {
12450 if (type == "=>") return cont(typeexpr)
12451 }
12452 function typeprop(type, value) {
12453 if (type == "variable" || cx.style == "keyword") {
12454 cx.marked = "property"
12455 return cont(typeprop)
12456 } else if (value == "?") {
12457 return cont(typeprop)
12458 } else if (type == ":") {
12459 return cont(typeexpr)
12460 } else if (type == "[") {
12461 return cont(expression, maybetype, expect("]"), typeprop)
12462 }
12463 }
12464 function typearg(type) {
12465 if (type == "variable") return cont(typearg)
12466 else if (type == ":") return cont(typeexpr)
12467 }
12468 function afterType(type, value) {
12469 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12470 if (value == "|" || type == ".") return cont(typeexpr)
12471 if (type == "[") return cont(expect("]"), afterType)
12472 if (value == "extends") return cont(typeexpr)
12473 }
12474 function maybeTypeArgs(_, value) {
12475 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12476 }
12477 function vardef() {
12478 return pass(pattern, maybetype, maybeAssign, vardefCont);
12479 }
12480 function pattern(type, value) {
12481 if (type == "modifier") return cont(pattern)
12482 if (type == "variable") { register(value); return cont(); }
12483 if (type == "spread") return cont(pattern);
12484 if (type == "[") return contCommasep(pattern, "]");
12485 if (type == "{") return contCommasep(proppattern, "}");
12486 }
12487 function proppattern(type, value) {
12488 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12489 register(value);
12490 return cont(maybeAssign);
12491 }
12492 if (type == "variable") cx.marked = "property";
12493 if (type == "spread") return cont(pattern);
12494 if (type == "}") return pass();
12495 return cont(expect(":"), pattern, maybeAssign);
12496 }
12497 function maybeAssign(_type, value) {
12498 if (value == "=") return cont(expressionNoComma);
12499 }
12500 function vardefCont(type) {
12501 if (type == ",") return cont(vardef);
12502 }
12503 function maybeelse(type, value) {
12504 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
12505 }
12506 function forspec(type) {
12507 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
12508 }
12509 function forspec1(type) {
12510 if (type == "var") return cont(vardef, expect(";"), forspec2);
12511 if (type == ";") return cont(forspec2);
12512 if (type == "variable") return cont(formaybeinof);
12513 return pass(expression, expect(";"), forspec2);
12514 }
12515 function formaybeinof(_type, value) {
12516 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12517 return cont(maybeoperatorComma, forspec2);
12518 }
12519 function forspec2(type, value) {
12520 if (type == ";") return cont(forspec3);
12521 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12522 return pass(expression, expect(";"), forspec3);
12523 }
12524 function forspec3(type) {
12525 if (type != ")") cont(expression);
12526 }
12527 function functiondef(type, value) {
12528 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
12529 if (type == "variable") {register(value); return cont(functiondef);}
12530 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
12531 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
12532 }
12533 function funarg(type) {
12534 if (type == "spread" || type == "modifier") return cont(funarg);
12535 return pass(pattern, maybetype, maybeAssign);
12536 }
12537 function classExpression(type, value) {
12538 // Class expressions may have an optional name.
12539 if (type == "variable") return className(type, value);
12540 return classNameAfter(type, value);
12541 }
12542 function className(type, value) {
12543 if (type == "variable") {register(value); return cont(classNameAfter);}
12544 }
12545 function classNameAfter(type, value) {
12546 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
12547 if (value == "extends" || value == "implements" || (isTS && type == ","))
12548 return cont(isTS ? typeexpr : expression, classNameAfter);
12549 if (type == "{") return cont(pushlex("}"), classBody, poplex);
12550 }
12551 function classBody(type, value) {
12552 if (type == "modifier" || type == "async" ||
12553 (type == "variable" &&
12554 (value == "static" || value == "get" || value == "set") &&
12555 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
12556 cx.marked = "keyword";
12557 return cont(classBody);
12558 }
12559 if (type == "variable") {
12560 cx.marked = "property";
12561 return cont(isTS ? classfield : functiondef, classBody);
12562 }
12563 if (type == "[")
12564 return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
12565 if (value == "*") {
12566 cx.marked = "keyword";
12567 return cont(classBody);
12568 }
12569 if (type == ";") return cont(classBody);
12570 if (type == "}") return cont();
12571 if (value == "@") return cont(expression, classBody)
12572 }
12573 function classfield(type, value) {
12574 if (value == "?") return cont(classfield)
12575 if (type == ":") return cont(typeexpr, maybeAssign)
12576 if (value == "=") return cont(expressionNoComma)
12577 return pass(functiondef)
12578 }
12579 function afterExport(type, value) {
12580 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
12581 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
12582 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
12583 return pass(statement);
12584 }
12585 function exportField(type, value) {
12586 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
12587 if (type == "variable") return pass(expressionNoComma, exportField);
12588 }
12589 function afterImport(type) {
12590 if (type == "string") return cont();
12591 return pass(importSpec, maybeMoreImports, maybeFrom);
12592 }
12593 function importSpec(type, value) {
12594 if (type == "{") return contCommasep(importSpec, "}");
12595 if (type == "variable") register(value);
12596 if (value == "*") cx.marked = "keyword";
12597 return cont(maybeAs);
12598 }
12599 function maybeMoreImports(type) {
12600 if (type == ",") return cont(importSpec, maybeMoreImports)
12601 }
12602 function maybeAs(_type, value) {
12603 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
12604 }
12605 function maybeFrom(_type, value) {
12606 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
12607 }
12608 function arrayLiteral(type) {
12609 if (type == "]") return cont();
12610 return pass(commasep(expressionNoComma, "]"));
12611 }
12612
12613 function isContinuedStatement(state, textAfter) {
12614 return state.lastType == "operator" || state.lastType == "," ||
12615 isOperatorChar.test(textAfter.charAt(0)) ||
12616 /[,.]/.test(textAfter.charAt(0));
12617 }
12618
12619 function expressionAllowed(stream, state, backUp) {
12620 return state.tokenize == tokenBase &&
12621 /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
12622 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
12623 }
12624
12625 // Interface
12626
12627 return {
12628 startState: function(basecolumn) {
12629 var state = {
12630 tokenize: tokenBase,
12631 lastType: "sof",
12632 cc: [],
12633 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
12634 localVars: parserConfig.localVars,
12635 context: parserConfig.localVars && {vars: parserConfig.localVars},
12636 indented: basecolumn || 0
12637 };
12638 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
12639 state.globalVars = parserConfig.globalVars;
12640 return state;
12641 },
12642
12643 token: function(stream, state) {
12644 if (stream.sol()) {
12645 if (!state.lexical.hasOwnProperty("align"))
12646 state.lexical.align = false;
12647 state.indented = stream.indentation();
12648 findFatArrow(stream, state);
12649 }
12650 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
12651 var style = state.tokenize(stream, state);
12652 if (type == "comment") return style;
12653 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
12654 return parseJS(state, style, type, content, stream);
12655 },
12656
12657 indent: function(state, textAfter) {
12658 if (state.tokenize == tokenComment) return CodeMirror.Pass;
12659 if (state.tokenize != tokenBase) return 0;
12660 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
12661 // Kludge to prevent 'maybelse' from blocking lexical scope pops
12662 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
12663 var c = state.cc[i];
12664 if (c == poplex) lexical = lexical.prev;
12665 else if (c != maybeelse) break;
12666 }
12667 while ((lexical.type == "stat" || lexical.type == "form") &&
12668 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
12669 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
12670 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
12671 lexical = lexical.prev;
12672 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
12673 lexical = lexical.prev;
12674 var type = lexical.type, closing = firstChar == type;
12675
12676 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
12677 else if (type == "form" && firstChar == "{") return lexical.indented;
12678 else if (type == "form") return lexical.indented + indentUnit;
12679 else if (type == "stat")
12680 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
12681 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
12682 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
12683 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
12684 else return lexical.indented + (closing ? 0 : indentUnit);
12685 },
12686
12687 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
12688 blockCommentStart: jsonMode ? null : "/*",
12689 blockCommentEnd: jsonMode ? null : "*/",
12690 lineComment: jsonMode ? null : "//",
12691 fold: "brace",
12692 closeBrackets: "()[]{}''\"\"``",
12693
12694 helperType: jsonMode ? "json" : "javascript",
12695 jsonldMode: jsonldMode,
12696 jsonMode: jsonMode,
12697
12698 expressionAllowed: expressionAllowed,
12699
12700 skipExpression: function(state) {
12701 var top = state.cc[state.cc.length - 1]
12702 if (top == expression || top == expressionNoComma) state.cc.pop()
12703 }
12704 };
12705 });
12706
12707 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
12708
12709 CodeMirror.defineMIME("text/javascript", "javascript");
12710 CodeMirror.defineMIME("text/ecmascript", "javascript");
12711 CodeMirror.defineMIME("application/javascript", "javascript");
12712 CodeMirror.defineMIME("application/x-javascript", "javascript");
12713 CodeMirror.defineMIME("application/ecmascript", "javascript");
12714 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
12715 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
12716 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
12717 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
12718 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
12719
12720 });
12721
12722 },{"../../lib/codemirror":12}],16:[function(require,module,exports){
12723 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12724 // Distributed under an MIT license: http://codemirror.net/LICENSE
12725
12726 (function(mod) {
12727 if (typeof exports == "object" && typeof module == "object") // CommonJS
12728 mod(require("../../lib/codemirror"));
12729 else if (typeof define == "function" && define.amd) // AMD
12730 define(["../../lib/codemirror"], mod);
12731 else // Plain browser env
12732 mod(CodeMirror);
12733 })(function(CodeMirror) {
12734 "use strict";
12735
12736 var htmlConfig = {
12737 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
12738 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
12739 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
12740 'track': true, 'wbr': true, 'menuitem': true},
12741 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
12742 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
12743 'th': true, 'tr': true},
12744 contextGrabbers: {
12745 'dd': {'dd': true, 'dt': true},
12746 'dt': {'dd': true, 'dt': true},
12747 'li': {'li': true},
12748 'option': {'option': true, 'optgroup': true},
12749 'optgroup': {'optgroup': true},
12750 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
12751 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
12752 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
12753 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
12754 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
12755 'rp': {'rp': true, 'rt': true},
12756 'rt': {'rp': true, 'rt': true},
12757 'tbody': {'tbody': true, 'tfoot': true},
12758 'td': {'td': true, 'th': true},
12759 'tfoot': {'tbody': true},
12760 'th': {'td': true, 'th': true},
12761 'thead': {'tbody': true, 'tfoot': true},
12762 'tr': {'tr': true}
12763 },
12764 doNotIndent: {"pre": true},
12765 allowUnquoted: true,
12766 allowMissing: true,
12767 caseFold: true
12768 }
12769
12770 var xmlConfig = {
12771 autoSelfClosers: {},
12772 implicitlyClosed: {},
12773 contextGrabbers: {},
12774 doNotIndent: {},
12775 allowUnquoted: false,
12776 allowMissing: false,
12777 caseFold: false
12778 }
12779
12780 CodeMirror.defineMode("xml", function(editorConf, config_) {
12781 var indentUnit = editorConf.indentUnit
12782 var config = {}
12783 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
12784 for (var prop in defaults) config[prop] = defaults[prop]
12785 for (var prop in config_) config[prop] = config_[prop]
12786
12787 // Return variables for tokenizers
12788 var type, setStyle;
12789
12790 function inText(stream, state) {
12791 function chain(parser) {
12792 state.tokenize = parser;
12793 return parser(stream, state);
12794 }
12795
12796 var ch = stream.next();
12797 if (ch == "<") {
12798 if (stream.eat("!")) {
12799 if (stream.eat("[")) {
12800 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
12801 else return null;
12802 } else if (stream.match("--")) {
12803 return chain(inBlock("comment", "-->"));
12804 } else if (stream.match("DOCTYPE", true, true)) {
12805 stream.eatWhile(/[\w\._\-]/);
12806 return chain(doctype(1));
12807 } else {
12808 return null;
12809 }
12810 } else if (stream.eat("?")) {
12811 stream.eatWhile(/[\w\._\-]/);
12812 state.tokenize = inBlock("meta", "?>");
12813 return "meta";
12814 } else {
12815 type = stream.eat("/") ? "closeTag" : "openTag";
12816 state.tokenize = inTag;
12817 return "tag bracket";
12818 }
12819 } else if (ch == "&") {
12820 var ok;
12821 if (stream.eat("#")) {
12822 if (stream.eat("x")) {
12823 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
12824 } else {
12825 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
12826 }
12827 } else {
12828 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
12829 }
12830 return ok ? "atom" : "error";
12831 } else {
12832 stream.eatWhile(/[^&<]/);
12833 return null;
12834 }
12835 }
12836 inText.isInText = true;
12837
12838 function inTag(stream, state) {
12839 var ch = stream.next();
12840 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
12841 state.tokenize = inText;
12842 type = ch == ">" ? "endTag" : "selfcloseTag";
12843 return "tag bracket";
12844 } else if (ch == "=") {
12845 type = "equals";
12846 return null;
12847 } else if (ch == "<") {
12848 state.tokenize = inText;
12849 state.state = baseState;
12850 state.tagName = state.tagStart = null;
12851 var next = state.tokenize(stream, state);
12852 return next ? next + " tag error" : "tag error";
12853 } else if (/[\'\"]/.test(ch)) {
12854 state.tokenize = inAttribute(ch);
12855 state.stringStartCol = stream.column();
12856 return state.tokenize(stream, state);
12857 } else {
12858 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
12859 return "word";
12860 }
12861 }
12862
12863 function inAttribute(quote) {
12864 var closure = function(stream, state) {
12865 while (!stream.eol()) {
12866 if (stream.next() == quote) {
12867 state.tokenize = inTag;
12868 break;
12869 }
12870 }
12871 return "string";
12872 };
12873 closure.isInAttribute = true;
12874 return closure;
12875 }
12876
12877 function inBlock(style, terminator) {
12878 return function(stream, state) {
12879 while (!stream.eol()) {
12880 if (stream.match(terminator)) {
12881 state.tokenize = inText;
12882 break;
12883 }
12884 stream.next();
12885 }
12886 return style;
12887 };
12888 }
12889 function doctype(depth) {
12890 return function(stream, state) {
12891 var ch;
12892 while ((ch = stream.next()) != null) {
12893 if (ch == "<") {
12894 state.tokenize = doctype(depth + 1);
12895 return state.tokenize(stream, state);
12896 } else if (ch == ">") {
12897 if (depth == 1) {
12898 state.tokenize = inText;
12899 break;
12900 } else {
12901 state.tokenize = doctype(depth - 1);
12902 return state.tokenize(stream, state);
12903 }
12904 }
12905 }
12906 return "meta";
12907 };
12908 }
12909
12910 function Context(state, tagName, startOfLine) {
12911 this.prev = state.context;
12912 this.tagName = tagName;
12913 this.indent = state.indented;
12914 this.startOfLine = startOfLine;
12915 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
12916 this.noIndent = true;
12917 }
12918 function popContext(state) {
12919 if (state.context) state.context = state.context.prev;
12920 }
12921 function maybePopContext(state, nextTagName) {
12922 var parentTagName;
12923 while (true) {
12924 if (!state.context) {
12925 return;
12926 }
12927 parentTagName = state.context.tagName;
12928 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
12929 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
12930 return;
12931 }
12932 popContext(state);
12933 }
12934 }
12935
12936 function baseState(type, stream, state) {
12937 if (type == "openTag") {
12938 state.tagStart = stream.column();
12939 return tagNameState;
12940 } else if (type == "closeTag") {
12941 return closeTagNameState;
12942 } else {
12943 return baseState;
12944 }
12945 }
12946 function tagNameState(type, stream, state) {
12947 if (type == "word") {
12948 state.tagName = stream.current();
12949 setStyle = "tag";
12950 return attrState;
12951 } else {
12952 setStyle = "error";
12953 return tagNameState;
12954 }
12955 }
12956 function closeTagNameState(type, stream, state) {
12957 if (type == "word") {
12958 var tagName = stream.current();
12959 if (state.context && state.context.tagName != tagName &&
12960 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
12961 popContext(state);
12962 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
12963 setStyle = "tag";
12964 return closeState;
12965 } else {
12966 setStyle = "tag error";
12967 return closeStateErr;
12968 }
12969 } else {
12970 setStyle = "error";
12971 return closeStateErr;
12972 }
12973 }
12974
12975 function closeState(type, _stream, state) {
12976 if (type != "endTag") {
12977 setStyle = "error";
12978 return closeState;
12979 }
12980 popContext(state);
12981 return baseState;
12982 }
12983 function closeStateErr(type, stream, state) {
12984 setStyle = "error";
12985 return closeState(type, stream, state);
12986 }
12987
12988 function attrState(type, _stream, state) {
12989 if (type == "word") {
12990 setStyle = "attribute";
12991 return attrEqState;
12992 } else if (type == "endTag" || type == "selfcloseTag") {
12993 var tagName = state.tagName, tagStart = state.tagStart;
12994 state.tagName = state.tagStart = null;
12995 if (type == "selfcloseTag" ||
12996 config.autoSelfClosers.hasOwnProperty(tagName)) {
12997 maybePopContext(state, tagName);
12998 } else {
12999 maybePopContext(state, tagName);
13000 state.context = new Context(state, tagName, tagStart == state.indented);
13001 }
13002 return baseState;
13003 }
13004 setStyle = "error";
13005 return attrState;
13006 }
13007 function attrEqState(type, stream, state) {
13008 if (type == "equals") return attrValueState;
13009 if (!config.allowMissing) setStyle = "error";
13010 return attrState(type, stream, state);
13011 }
13012 function attrValueState(type, stream, state) {
13013 if (type == "string") return attrContinuedState;
13014 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13015 setStyle = "error";
13016 return attrState(type, stream, state);
13017 }
13018 function attrContinuedState(type, stream, state) {
13019 if (type == "string") return attrContinuedState;
13020 return attrState(type, stream, state);
13021 }
13022
13023 return {
13024 startState: function(baseIndent) {
13025 var state = {tokenize: inText,
13026 state: baseState,
13027 indented: baseIndent || 0,
13028 tagName: null, tagStart: null,
13029 context: null}
13030 if (baseIndent != null) state.baseIndent = baseIndent
13031 return state
13032 },
13033
13034 token: function(stream, state) {
13035 if (!state.tagName && stream.sol())
13036 state.indented = stream.indentation();
13037
13038 if (stream.eatSpace()) return null;
13039 type = null;
13040 var style = state.tokenize(stream, state);
13041 if ((style || type) && style != "comment") {
13042 setStyle = null;
13043 state.state = state.state(type || style, stream, state);
13044 if (setStyle)
13045 style = setStyle == "error" ? style + " error" : setStyle;
13046 }
13047 return style;
13048 },
13049
13050 indent: function(state, textAfter, fullLine) {
13051 var context = state.context;
13052 // Indent multi-line strings (e.g. css).
13053 if (state.tokenize.isInAttribute) {
13054 if (state.tagStart == state.indented)
13055 return state.stringStartCol + 1;
13056 else
13057 return state.indented + indentUnit;
13058 }
13059 if (context && context.noIndent) return CodeMirror.Pass;
13060 if (state.tokenize != inTag && state.tokenize != inText)
13061 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13062 // Indent the starts of attribute names.
13063 if (state.tagName) {
13064 if (config.multilineTagIndentPastTag !== false)
13065 return state.tagStart + state.tagName.length + 2;
13066 else
13067 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13068 }
13069 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13070 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13071 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13072 while (context) {
13073 if (context.tagName == tagAfter[2]) {
13074 context = context.prev;
13075 break;
13076 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13077 context = context.prev;
13078 } else {
13079 break;
13080 }
13081 }
13082 } else if (tagAfter) { // Opening tag spotted
13083 while (context) {
13084 var grabbers = config.contextGrabbers[context.tagName];
13085 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13086 context = context.prev;
13087 else
13088 break;
13089 }
13090 }
13091 while (context && context.prev && !context.startOfLine)
13092 context = context.prev;
13093 if (context) return context.indent + indentUnit;
13094 else return state.baseIndent || 0;
13095 },
13096
13097 electricInput: /<\/[\s\w:]+>$/,
13098 blockCommentStart: "<!--",
13099 blockCommentEnd: "-->",
13100
13101 configuration: config.htmlMode ? "html" : "xml",
13102 helperType: config.htmlMode ? "html" : "xml",
13103
13104 skipAttribute: function(state) {
13105 if (state.state == attrValueState)
13106 state.state = attrState
13107 }
13108 };
13109 });
13110
13111 CodeMirror.defineMIME("text/xml", "xml");
13112 CodeMirror.defineMIME("application/xml", "xml");
13113 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13114 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13115
13116 });
13117
13118 },{"../../lib/codemirror":12}],17:[function(require,module,exports){
13119 (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);};};}});
13120
13121
13122 },{}],18:[function(require,module,exports){
13123 /*
13124
13125 Style HTML
13126 ---------------
13127
13128 Written by Nochum Sossonko, (nsossonko@hotmail.com)
13129
13130 Based on code initially developed by: Einar Lielmanis, <elfz@laacz.lv>
13131 http://jsbeautifier.org/
13132
13133
13134 You are free to use this in any way you want, in case you find this useful or working for you.
13135
13136 Usage:
13137 style_html(html_source);
13138
13139 style_html(html_source, options);
13140
13141 The options are:
13142 indent_size (default 4) — indentation size,
13143 indent_char (default space) — character to indent with,
13144 max_char (default 70) - maximum amount of characters per line,
13145 brace_style (default "collapse") - "collapse" | "expand" | "end-expand"
13146 put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
13147 unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
13148 indent_scripts (default normal) - "keep"|"separate"|"normal"
13149
13150 e.g.
13151
13152 style_html(html_source, {
13153 'indent_size': 2,
13154 'indent_char': ' ',
13155 'max_char': 78,
13156 'brace_style': 'expand',
13157 'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u']
13158 });
13159 */
13160
13161 function style_html(html_source, options) {
13162 //Wrapper function to invoke all the necessary constructors and deal with the output.
13163
13164 var multi_parser,
13165 indent_size,
13166 indent_character,
13167 max_char,
13168 brace_style,
13169 unformatted;
13170
13171 options = options || {};
13172 indent_size = options.indent_size || 4;
13173 indent_character = options.indent_char || ' ';
13174 brace_style = options.brace_style || 'collapse';
13175 max_char = options.max_char == 0 ? Infinity : options.max_char || 70;
13176 unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
13177
13178 function Parser() {
13179
13180 this.pos = 0; //Parser position
13181 this.token = '';
13182 this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
13183 this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
13184 parent: 'parent1',
13185 parentcount: 1,
13186 parent1: ''
13187 };
13188 this.tag_type = '';
13189 this.token_text = this.last_token = this.last_text = this.token_type = '';
13190
13191 this.Utils = { //Uilities made available to the various functions
13192 whitespace: "\n\r\t ".split(''),
13193 single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML
13194 extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them
13195 in_array: function (what, arr) {
13196 for (var i=0; i<arr.length; i++) {
13197 if (what === arr[i]) {
13198 return true;
13199 }
13200 }
13201 return false;
13202 }
13203 }
13204
13205 this.get_content = function () { //function to capture regular content between tags
13206
13207 var input_char = '',
13208 content = [],
13209 space = false; //if a space is needed
13210
13211 while (this.input.charAt(this.pos) !== '<') {
13212 if (this.pos >= this.input.length) {
13213 return content.length?content.join(''):['', 'TK_EOF'];
13214 }
13215
13216 input_char = this.input.charAt(this.pos);
13217 this.pos++;
13218 this.line_char_count++;
13219
13220 if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
13221 if (content.length) {
13222 space = true;
13223 }
13224 this.line_char_count--;
13225 continue; //don't want to insert unnecessary space
13226 }
13227 else if (space) {
13228 if (this.line_char_count >= this.max_char) { //insert a line when the max_char is reached
13229 content.push('\n');
13230 for (var i=0; i<this.indent_level; i++) {
13231 content.push(this.indent_string);
13232 }
13233 this.line_char_count = 0;
13234 }
13235 else{
13236 content.push(' ');
13237 this.line_char_count++;
13238 }
13239 space = false;
13240 }
13241 content.push(input_char); //letter at-a-time (or string) inserted to an array
13242 }
13243 return content.length?content.join(''):'';
13244 }
13245
13246 this.get_contents_to = function (name) { //get the full content of a script or style to pass to js_beautify
13247 if (this.pos == this.input.length) {
13248 return ['', 'TK_EOF'];
13249 }
13250 var input_char = '';
13251 var content = '';
13252 var reg_match = new RegExp('\<\/' + name + '\\s*\>', 'igm');
13253 reg_match.lastIndex = this.pos;
13254 var reg_array = reg_match.exec(this.input);
13255 var end_script = reg_array?reg_array.index:this.input.length; //absolute end of script
13256 if(this.pos < end_script) { //get everything in between the script tags
13257 content = this.input.substring(this.pos, end_script);
13258 this.pos = end_script;
13259 }
13260 return content;
13261 }
13262
13263 this.record_tag = function (tag){ //function to record a tag and its parent in this.tags Object
13264 if (this.tags[tag + 'count']) { //check for the existence of this tag type
13265 this.tags[tag + 'count']++;
13266 this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
13267 }
13268 else { //otherwise initialize this tag type
13269 this.tags[tag + 'count'] = 1;
13270 this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
13271 }
13272 this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
13273 this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
13274 }
13275
13276 this.retrieve_tag = function (tag) { //function to retrieve the opening tag to the corresponding closer
13277 if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
13278 var temp_parent = this.tags.parent; //check to see if it's a closable tag.
13279 while (temp_parent) { //till we reach '' (the initial value);
13280 if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
13281 break;
13282 }
13283 temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
13284 }
13285 if (temp_parent) { //if we caught something
13286 this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
13287 this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
13288 }
13289 delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
13290 delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
13291 if (this.tags[tag + 'count'] == 1) {
13292 delete this.tags[tag + 'count'];
13293 }
13294 else {
13295 this.tags[tag + 'count']--;
13296 }
13297 }
13298 }
13299
13300 this.get_tag = function () { //function to get a full tag and parse its type
13301 var input_char = '',
13302 content = [],
13303 space = false,
13304 tag_start, tag_end;
13305
13306 do {
13307 if (this.pos >= this.input.length) {
13308 return content.length?content.join(''):['', 'TK_EOF'];
13309 }
13310
13311 input_char = this.input.charAt(this.pos);
13312 this.pos++;
13313 this.line_char_count++;
13314
13315 if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
13316 space = true;
13317 this.line_char_count--;
13318 continue;
13319 }
13320
13321 if (input_char === "'" || input_char === '"') {
13322 if (!content[1] || content[1] !== '!') { //if we're in a comment strings don't get treated specially
13323 input_char += this.get_unformatted(input_char);
13324 space = true;
13325 }
13326 }
13327
13328 if (input_char === '=') { //no space before =
13329 space = false;
13330 }
13331
13332 if (content.length && content[content.length-1] !== '=' && input_char !== '>'
13333 && space) { //no space after = or before >
13334 if (this.line_char_count >= this.max_char) {
13335 this.print_newline(false, content);
13336 this.line_char_count = 0;
13337 }
13338 else {
13339 content.push(' ');
13340 this.line_char_count++;
13341 }
13342 space = false;
13343 }
13344 if (input_char === '<') {
13345 tag_start = this.pos - 1;
13346 }
13347 content.push(input_char); //inserts character at-a-time (or string)
13348 } while (input_char !== '>');
13349
13350 var tag_complete = content.join('');
13351 var tag_index;
13352 if (tag_complete.indexOf(' ') != -1) { //if there's whitespace, thats where the tag name ends
13353 tag_index = tag_complete.indexOf(' ');
13354 }
13355 else { //otherwise go with the tag ending
13356 tag_index = tag_complete.indexOf('>');
13357 }
13358 var tag_check = tag_complete.substring(1, tag_index).toLowerCase();
13359 if (tag_complete.charAt(tag_complete.length-2) === '/' ||
13360 this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
13361 this.tag_type = 'SINGLE';
13362 }
13363 else if (tag_check === 'script') { //for later script handling
13364 this.record_tag(tag_check);
13365 this.tag_type = 'SCRIPT';
13366 }
13367 else if (tag_check === 'style') { //for future style handling (for now it justs uses get_content)
13368 this.record_tag(tag_check);
13369 this.tag_type = 'STYLE';
13370 }
13371 else if (this.Utils.in_array(tag_check, unformatted)) { // do not reformat the "unformatted" tags
13372 var comment = this.get_unformatted('</'+tag_check+'>', tag_complete); //...delegate to get_unformatted function
13373 content.push(comment);
13374 // Preserve collapsed whitespace either before or after this tag.
13375 if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)){
13376 content.splice(0, 0, this.input.charAt(tag_start - 1));
13377 }
13378 tag_end = this.pos - 1;
13379 if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)){
13380 content.push(this.input.charAt(tag_end + 1));
13381 }
13382 this.tag_type = 'SINGLE';
13383 }
13384 else if (tag_check.charAt(0) === '!') { //peek for <!-- comment
13385 if (tag_check.indexOf('[if') != -1) { //peek for <!--[if conditional comment
13386 if (tag_complete.indexOf('!IE') != -1) { //this type needs a closing --> so...
13387 var comment = this.get_unformatted('-->', tag_complete); //...delegate to get_unformatted
13388 content.push(comment);
13389 }
13390 this.tag_type = 'START';
13391 }
13392 else if (tag_check.indexOf('[endif') != -1) {//peek for <!--[endif end conditional comment
13393 this.tag_type = 'END';
13394 this.unindent();
13395 }
13396 else if (tag_check.indexOf('[cdata[') != -1) { //if it's a <[cdata[ comment...
13397 var comment = this.get_unformatted(']]>', tag_complete); //...delegate to get_unformatted function
13398 content.push(comment);
13399 this.tag_type = 'SINGLE'; //<![CDATA[ comments are treated like single tags
13400 }
13401 else {
13402 var comment = this.get_unformatted('-->', tag_complete);
13403 content.push(comment);
13404 this.tag_type = 'SINGLE';
13405 }
13406 }
13407 else {
13408 if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
13409 this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
13410 this.tag_type = 'END';
13411 }
13412 else { //otherwise it's a start-tag
13413 this.record_tag(tag_check); //push it on the tag stack
13414 this.tag_type = 'START';
13415 }
13416 if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
13417 this.print_newline(true, this.output);
13418 }
13419 }
13420 return content.join(''); //returns fully formatted tag
13421 }
13422
13423 this.get_unformatted = function (delimiter, orig_tag) { //function to return unformatted content in its entirety
13424
13425 if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) != -1) {
13426 return '';
13427 }
13428 var input_char = '';
13429 var content = '';
13430 var space = true;
13431 do {
13432
13433 if (this.pos >= this.input.length) {
13434 return content;
13435 }
13436
13437 input_char = this.input.charAt(this.pos);
13438 this.pos++
13439
13440 if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
13441 if (!space) {
13442 this.line_char_count--;
13443 continue;
13444 }
13445 if (input_char === '\n' || input_char === '\r') {
13446 content += '\n';
13447 /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
13448 for (var i=0; i<this.indent_level; i++) {
13449 content += this.indent_string;
13450 }
13451 space = false; //...and make sure other indentation is erased
13452 */
13453 this.line_char_count = 0;
13454 continue;
13455 }
13456 }
13457 content += input_char;
13458 this.line_char_count++;
13459 space = true;
13460
13461
13462 } while (content.toLowerCase().indexOf(delimiter) == -1);
13463 return content;
13464 }
13465
13466 this.get_token = function () { //initial handler for token-retrieval
13467 var token;
13468
13469 if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
13470 var type = this.last_token.substr(7)
13471 token = this.get_contents_to(type);
13472 if (typeof token !== 'string') {
13473 return token;
13474 }
13475 return [token, 'TK_' + type];
13476 }
13477 if (this.current_mode === 'CONTENT') {
13478 token = this.get_content();
13479 if (typeof token !== 'string') {
13480 return token;
13481 }
13482 else {
13483 return [token, 'TK_CONTENT'];
13484 }
13485 }
13486
13487 if (this.current_mode === 'TAG') {
13488 token = this.get_tag();
13489 if (typeof token !== 'string') {
13490 return token;
13491 }
13492 else {
13493 var tag_name_type = 'TK_TAG_' + this.tag_type;
13494 return [token, tag_name_type];
13495 }
13496 }
13497 }
13498
13499 this.get_full_indent = function (level) {
13500 level = this.indent_level + level || 0;
13501 if (level < 1)
13502 return '';
13503
13504 return Array(level + 1).join(this.indent_string);
13505 }
13506
13507
13508 this.printer = function (js_source, indent_character, indent_size, max_char, brace_style) { //handles input/output and some other printing functions
13509
13510 this.input = js_source || ''; //gets the input for the Parser
13511 this.output = [];
13512 this.indent_character = indent_character;
13513 this.indent_string = '';
13514 this.indent_size = indent_size;
13515 this.brace_style = brace_style;
13516 this.indent_level = 0;
13517 this.max_char = max_char;
13518 this.line_char_count = 0; //count to see if max_char was exceeded
13519
13520 for (var i=0; i<this.indent_size; i++) {
13521 this.indent_string += this.indent_character;
13522 }
13523
13524 this.print_newline = function (ignore, arr) {
13525 this.line_char_count = 0;
13526 if (!arr || !arr.length) {
13527 return;
13528 }
13529 if (!ignore) { //we might want the extra line
13530 while (this.Utils.in_array(arr[arr.length-1], this.Utils.whitespace)) {
13531 arr.pop();
13532 }
13533 }
13534 arr.push('\n');
13535 for (var i=0; i<this.indent_level; i++) {
13536 arr.push(this.indent_string);
13537 }
13538 }
13539
13540 this.print_token = function (text) {
13541 this.output.push(text);
13542 }
13543
13544 this.indent = function () {
13545 this.indent_level++;
13546 }
13547
13548 this.unindent = function () {
13549 if (this.indent_level > 0) {
13550 this.indent_level--;
13551 }
13552 }
13553 }
13554 return this;
13555 }
13556
13557 /*_____________________--------------------_____________________*/
13558
13559 multi_parser = new Parser(); //wrapping functions Parser
13560 multi_parser.printer(html_source, indent_character, indent_size, max_char, brace_style); //initialize starting values
13561
13562 while (true) {
13563 var t = multi_parser.get_token();
13564 multi_parser.token_text = t[0];
13565 multi_parser.token_type = t[1];
13566
13567 if (multi_parser.token_type === 'TK_EOF') {
13568 break;
13569 }
13570
13571 switch (multi_parser.token_type) {
13572 case 'TK_TAG_START':
13573 multi_parser.print_newline(false, multi_parser.output);
13574 multi_parser.print_token(multi_parser.token_text);
13575 multi_parser.indent();
13576 multi_parser.current_mode = 'CONTENT';
13577 break;
13578 case 'TK_TAG_STYLE':
13579 case 'TK_TAG_SCRIPT':
13580 multi_parser.print_newline(false, multi_parser.output);
13581 multi_parser.print_token(multi_parser.token_text);
13582 multi_parser.current_mode = 'CONTENT';
13583 break;
13584 case 'TK_TAG_END':
13585 //Print new line only if the tag has no content and has child
13586 if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
13587 var tag_name = multi_parser.token_text.match(/\w+/)[0];
13588 var tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length -1].match(/<\s*(\w+)/);
13589 if (tag_extracted_from_last_output === null || tag_extracted_from_last_output[1] !== tag_name)
13590 multi_parser.print_newline(true, multi_parser.output);
13591 }
13592 multi_parser.print_token(multi_parser.token_text);
13593 multi_parser.current_mode = 'CONTENT';
13594 break;
13595 case 'TK_TAG_SINGLE':
13596 // Don't add a newline before elements that should remain unformatted.
13597 var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
13598 if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)){
13599 multi_parser.print_newline(false, multi_parser.output);
13600 }
13601 multi_parser.print_token(multi_parser.token_text);
13602 multi_parser.current_mode = 'CONTENT';
13603 break;
13604 case 'TK_CONTENT':
13605 if (multi_parser.token_text !== '') {
13606 multi_parser.print_token(multi_parser.token_text);
13607 }
13608 multi_parser.current_mode = 'TAG';
13609 break;
13610 case 'TK_STYLE':
13611 case 'TK_SCRIPT':
13612 if (multi_parser.token_text !== '') {
13613 multi_parser.output.push('\n');
13614 var text = multi_parser.token_text;
13615 if (multi_parser.token_type == 'TK_SCRIPT') {
13616 var _beautifier = typeof js_beautify == 'function' && js_beautify;
13617 } else if (multi_parser.token_type == 'TK_STYLE') {
13618 var _beautifier = typeof css_beautify == 'function' && css_beautify;
13619 }
13620
13621 if (options.indent_scripts == "keep") {
13622 var script_indent_level = 0;
13623 } else if (options.indent_scripts == "separate") {
13624 var script_indent_level = -multi_parser.indent_level;
13625 } else {
13626 var script_indent_level = 1;
13627 }
13628
13629 var indentation = multi_parser.get_full_indent(script_indent_level);
13630 if (_beautifier) {
13631 // call the Beautifier if avaliable
13632 text = _beautifier(text.replace(/^\s*/, indentation), options);
13633 } else {
13634 // simply indent the string otherwise
13635 var white = text.match(/^\s*/)[0];
13636 var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
13637 var reindent = multi_parser.get_full_indent(script_indent_level -_level);
13638 text = text.replace(/^\s*/, indentation)
13639 .replace(/\r\n|\r|\n/g, '\n' + reindent)
13640 .replace(/\s*$/, '');
13641 }
13642 if (text) {
13643 multi_parser.print_token(text);
13644 multi_parser.print_newline(true, multi_parser.output);
13645 }
13646 }
13647 multi_parser.current_mode = 'TAG';
13648 break;
13649 }
13650 multi_parser.last_token = multi_parser.token_type;
13651 multi_parser.last_text = multi_parser.token_text;
13652 }
13653 return multi_parser.output.join('');
13654 }
13655
13656 module.exports = {
13657 prettyPrint: style_html
13658 };
13659 },{}],19:[function(require,module,exports){
13660 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;
13661
13662
13663 },{}],20:[function(require,module,exports){
13664 (function (global, factory) {
13665 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13666 typeof define === 'function' && define.amd ? define(factory) :
13667 (global.preactRenderToString = factory());
13668 }(this, (function () {
13669
13670 var NON_DIMENSION_PROPS = {
13671 boxFlex: 1, boxFlexGroup: 1, columnCount: 1, fillOpacity: 1, flex: 1, flexGrow: 1,
13672 flexPositive: 1, flexShrink: 1, flexNegative: 1, fontWeight: 1, lineClamp: 1, lineHeight: 1,
13673 opacity: 1, order: 1, orphans: 1, strokeOpacity: 1, widows: 1, zIndex: 1, zoom: 1
13674 };
13675
13676 var ESC = {
13677 '<': '&lt;',
13678 '>': '&gt;',
13679 '"': '&quot;',
13680 '&': '&amp;'
13681 };
13682
13683 var objectKeys = Object.keys || function (obj) {
13684 var keys = [];
13685 for (var i in obj) {
13686 if (obj.hasOwnProperty(i)) keys.push(i);
13687 }return keys;
13688 };
13689
13690 var encodeEntities = function (s) {
13691 return String(s).replace(/[<>"&]/g, escapeChar);
13692 };
13693
13694 var escapeChar = function (a) {
13695 return ESC[a] || a;
13696 };
13697
13698 var falsey = function (v) {
13699 return v == null || v === false;
13700 };
13701
13702 var memoize = function (fn) {
13703 var mem = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
13704 return function (v) {
13705 return mem[v] || (mem[v] = fn(v));
13706 };
13707 };
13708
13709 var indent = function (s, char) {
13710 return String(s).replace(/(\n+)/g, '$1' + (char || '\t'));
13711 };
13712
13713 var isLargeString = function (s, length, ignoreLines) {
13714 return String(s).length > (length || 40) || !ignoreLines && String(s).indexOf('\n') !== -1 || String(s).indexOf('<') !== -1;
13715 };
13716
13717 function styleObjToCss(s) {
13718 var str = '';
13719 for (var prop in s) {
13720 var val = s[prop];
13721 if (val != null) {
13722 if (str) str += ' ';
13723 str += jsToCss(prop);
13724 str += ': ';
13725 str += val;
13726 if (typeof val === 'number' && !NON_DIMENSION_PROPS[prop]) {
13727 str += 'px';
13728 }
13729 str += ';';
13730 }
13731 }
13732 return str || undefined;
13733 }
13734
13735 function hashToClassName(c) {
13736 var str = '';
13737 for (var prop in c) {
13738 if (c[prop]) {
13739 if (str) str += ' ';
13740 str += prop;
13741 }
13742 }
13743 return str;
13744 }
13745
13746 var jsToCss = memoize(function (s) {
13747 return s.replace(/([A-Z])/g, '-$1').toLowerCase();
13748 });
13749
13750 function assign(obj, props) {
13751 for (var i in props) {
13752 obj[i] = props[i];
13753 }return obj;
13754 }
13755
13756 function getNodeProps(vnode) {
13757 var defaultProps = vnode.nodeName.defaultProps,
13758 props = assign({}, defaultProps || vnode.attributes);
13759 if (defaultProps) assign(props, vnode.attributes);
13760 if (vnode.children) props.children = vnode.children;
13761 return props;
13762 }
13763
13764 var SHALLOW = { shallow: true };
13765
13766 var UNNAMED = [];
13767
13768 var EMPTY = {};
13769
13770 var VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
13771
13772 renderToString.render = renderToString;
13773
13774 var shallowRender = function (vnode, context) {
13775 return renderToString(vnode, context, SHALLOW);
13776 };
13777
13778 function renderToString(vnode, context, opts, inner, isSvgMode) {
13779 var _ref = vnode || EMPTY;
13780
13781 var nodeName = _ref.nodeName;
13782 var attributes = _ref.attributes;
13783 var children = _ref.children;
13784 var isComponent = false;
13785 context = context || {};
13786 opts = opts || {};
13787
13788 var pretty = opts.pretty,
13789 indentChar = typeof pretty === 'string' ? pretty : '\t';
13790
13791 if (vnode == null || vnode === false) {
13792 return '';
13793 }
13794
13795 if (!nodeName) {
13796 return encodeEntities(vnode);
13797 }
13798
13799 if (typeof nodeName === 'function') {
13800 isComponent = true;
13801 if (opts.shallow && (inner || opts.renderRootComponent === false)) {
13802 nodeName = getComponentName(nodeName);
13803 } else {
13804 var props = getNodeProps(vnode),
13805 rendered = void 0;
13806
13807 if (!nodeName.prototype || typeof nodeName.prototype.render !== 'function') {
13808 rendered = nodeName(props, context);
13809 } else {
13810 var c = new nodeName(props, context);
13811
13812 c._disable = c.__x = true;
13813 c.props = props;
13814 c.context = context;
13815 if (c.componentWillMount) c.componentWillMount();
13816 rendered = c.render(c.props, c.state, c.context);
13817
13818 if (c.getChildContext) {
13819 context = assign(assign({}, context), c.getChildContext());
13820 }
13821 }
13822
13823 return renderToString(rendered, context, opts, opts.shallowHighOrder !== false);
13824 }
13825 }
13826
13827 var s = '',
13828 html = void 0;
13829
13830 if (attributes) {
13831 var attrs = objectKeys(attributes);
13832
13833 if (opts && opts.sortAttributes === true) attrs.sort();
13834
13835 for (var i = 0; i < attrs.length; i++) {
13836 var name = attrs[i],
13837 v = attributes[name];
13838 if (name === 'children') continue;
13839 if (!(opts && opts.allAttributes) && (name === 'key' || name === 'ref')) continue;
13840
13841 if (name === 'className') {
13842 if (attributes['class']) continue;
13843 name = 'class';
13844 } else if (isSvgMode && name.match(/^xlink\:?(.+)/)) {
13845 name = name.toLowerCase().replace(/^xlink\:?(.+)/, 'xlink:$1');
13846 }
13847
13848 if (name === 'class' && v && typeof v === 'object') {
13849 v = hashToClassName(v);
13850 } else if (name === 'style' && v && typeof v === 'object') {
13851 v = styleObjToCss(v);
13852 }
13853
13854 var hooked = opts.attributeHook && opts.attributeHook(name, v, context, opts, isComponent);
13855 if (hooked || hooked === '') {
13856 s += hooked;
13857 continue;
13858 }
13859
13860 if (name === 'dangerouslySetInnerHTML') {
13861 html = v && v.__html;
13862 } else if ((v || v === 0 || v === '') && typeof v !== 'function') {
13863 if (v === true || v === '') {
13864 v = name;
13865
13866 if (!opts || !opts.xml) {
13867 s += ' ' + name;
13868 continue;
13869 }
13870 }
13871 s += ' ' + name + '="' + encodeEntities(v) + '"';
13872 }
13873 }
13874 }
13875
13876 var sub = s.replace(/^\n\s*/, ' ');
13877 if (sub !== s && !~sub.indexOf('\n')) s = sub;else if (pretty && ~s.indexOf('\n')) s += '\n';
13878
13879 s = '<' + nodeName + s + '>';
13880
13881 if (VOID_ELEMENTS.indexOf(nodeName) > -1) {
13882 s = s.replace(/>$/, ' />');
13883 }
13884
13885 if (html) {
13886 if (pretty && isLargeString(html)) {
13887 html = '\n' + indentChar + indent(html, indentChar);
13888 }
13889 s += html;
13890 } else {
13891 var len = children && children.length,
13892 pieces = [],
13893 hasLarge = ~s.indexOf('\n');
13894 for (var _i = 0; _i < len; _i++) {
13895 var child = children[_i];
13896 if (!falsey(child)) {
13897 var childSvgMode = nodeName === 'svg' ? true : nodeName === 'foreignObject' ? false : isSvgMode,
13898 ret = renderToString(child, context, opts, true, childSvgMode);
13899 if (!hasLarge && pretty && isLargeString(ret)) hasLarge = true;
13900 if (ret) pieces.push(ret);
13901 }
13902 }
13903 if (pretty && hasLarge) {
13904 for (var _i2 = pieces.length; _i2--;) {
13905 pieces[_i2] = '\n' + indentChar + indent(pieces[_i2], indentChar);
13906 }
13907 }
13908 if (pieces.length) {
13909 s += pieces.join('');
13910 } else if (opts && opts.xml) {
13911 return s.substring(0, s.length - 1) + ' />';
13912 }
13913 }
13914
13915 if (opts.jsx || VOID_ELEMENTS.indexOf(nodeName) === -1) {
13916 if (pretty && ~s.indexOf('\n')) s += '\n';
13917 s += '</' + nodeName + '>';
13918 }
13919
13920 return s;
13921 }
13922
13923 function getComponentName(component) {
13924 var proto = component.prototype,
13925 ctor = proto && proto.constructor;
13926 return component.displayName || component.name || proto && (proto.displayName || proto.name) || getFallbackComponentName(component);
13927 }
13928
13929 function getFallbackComponentName(component) {
13930 var str = Function.prototype.toString.call(component),
13931 name = (str.match(/^\s*function\s+([^\( ]+)/) || EMPTY)[1];
13932 if (!name) {
13933 var index = -1;
13934 for (var i = UNNAMED.length; i--;) {
13935 if (UNNAMED[i] === component) {
13936 index = i;
13937 break;
13938 }
13939 }
13940
13941 if (index < 0) {
13942 index = UNNAMED.push(component) - 1;
13943 }
13944 name = 'UnnamedComponent' + index;
13945 }
13946 return name;
13947 }
13948 renderToString.shallowRender = shallowRender;
13949
13950 return renderToString;
13951
13952 })));
13953
13954
13955 },{}],21:[function(require,module,exports){
13956 !function() {
13957 'use strict';
13958 function VNode() {}
13959 function h(nodeName, attributes) {
13960 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13961 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13962 if (attributes && null != attributes.children) {
13963 if (!stack.length) stack.push(attributes.children);
13964 delete attributes.children;
13965 }
13966 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13967 if ('boolean' == typeof child) child = null;
13968 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13969 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13970 lastSimple = simple;
13971 }
13972 var p = new VNode();
13973 p.nodeName = nodeName;
13974 p.children = children;
13975 p.attributes = null == attributes ? void 0 : attributes;
13976 p.key = null == attributes ? void 0 : attributes.key;
13977 if (void 0 !== options.vnode) options.vnode(p);
13978 return p;
13979 }
13980 function extend(obj, props) {
13981 for (var i in props) obj[i] = props[i];
13982 return obj;
13983 }
13984 function cloneElement(vnode, props) {
13985 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13986 }
13987 function enqueueRender(component) {
13988 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13989 }
13990 function rerender() {
13991 var p, list = items;
13992 items = [];
13993 while (p = list.pop()) if (p.__d) renderComponent(p);
13994 }
13995 function isSameNodeType(node, vnode, hydrating) {
13996 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13997 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13998 }
13999 function isNamedNode(node, nodeName) {
14000 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
14001 }
14002 function getNodeProps(vnode) {
14003 var props = extend({}, vnode.attributes);
14004 props.children = vnode.children;
14005 var defaultProps = vnode.nodeName.defaultProps;
14006 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
14007 return props;
14008 }
14009 function createNode(nodeName, isSvg) {
14010 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
14011 node.__n = nodeName;
14012 return node;
14013 }
14014 function removeNode(node) {
14015 var parentNode = node.parentNode;
14016 if (parentNode) parentNode.removeChild(node);
14017 }
14018 function setAccessor(node, name, old, value, isSvg) {
14019 if ('className' === name) name = 'class';
14020 if ('key' === name) ; else if ('ref' === name) {
14021 if (old) old(null);
14022 if (value) value(node);
14023 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
14024 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
14025 if (value && 'object' == typeof value) {
14026 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
14027 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
14028 }
14029 } else if ('dangerouslySetInnerHTML' === name) {
14030 if (value) node.innerHTML = value.__html || '';
14031 } else if ('o' == name[0] && 'n' == name[1]) {
14032 var useCapture = name !== (name = name.replace(/Capture$/, ''));
14033 name = name.toLowerCase().substring(2);
14034 if (value) {
14035 if (!old) node.addEventListener(name, eventProxy, useCapture);
14036 } else node.removeEventListener(name, eventProxy, useCapture);
14037 (node.__l || (node.__l = {}))[name] = value;
14038 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
14039 setProperty(node, name, null == value ? '' : value);
14040 if (null == value || !1 === value) node.removeAttribute(name);
14041 } else {
14042 var ns = isSvg && name !== (name = name.replace(/^xlink\:?/, ''));
14043 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);
14044 }
14045 }
14046 function setProperty(node, name, value) {
14047 try {
14048 node[name] = value;
14049 } catch (e) {}
14050 }
14051 function eventProxy(e) {
14052 return this.__l[e.type](options.event && options.event(e) || e);
14053 }
14054 function flushMounts() {
14055 var c;
14056 while (c = mounts.pop()) {
14057 if (options.afterMount) options.afterMount(c);
14058 if (c.componentDidMount) c.componentDidMount();
14059 }
14060 }
14061 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
14062 if (!diffLevel++) {
14063 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
14064 hydrating = null != dom && !('__preactattr_' in dom);
14065 }
14066 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
14067 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
14068 if (!--diffLevel) {
14069 hydrating = !1;
14070 if (!componentRoot) flushMounts();
14071 }
14072 return ret;
14073 }
14074 function idiff(dom, vnode, context, mountAll, componentRoot) {
14075 var out = dom, prevSvgMode = isSvgMode;
14076 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
14077 if ('string' == typeof vnode || 'number' == typeof vnode) {
14078 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
14079 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
14080 } else {
14081 out = document.createTextNode(vnode);
14082 if (dom) {
14083 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
14084 recollectNodeTree(dom, !0);
14085 }
14086 }
14087 out.__preactattr_ = !0;
14088 return out;
14089 }
14090 var vnodeName = vnode.nodeName;
14091 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
14092 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
14093 vnodeName = String(vnodeName);
14094 if (!dom || !isNamedNode(dom, vnodeName)) {
14095 out = createNode(vnodeName, isSvgMode);
14096 if (dom) {
14097 while (dom.firstChild) out.appendChild(dom.firstChild);
14098 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
14099 recollectNodeTree(dom, !0);
14100 }
14101 }
14102 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
14103 if (null == props) {
14104 props = out.__preactattr_ = {};
14105 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
14106 }
14107 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
14108 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
14109 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
14110 diffAttributes(out, vnode.attributes, props);
14111 isSvgMode = prevSvgMode;
14112 return out;
14113 }
14114 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
14115 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;
14116 if (0 !== len) for (var i = 0; i < len; i++) {
14117 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
14118 if (null != key) {
14119 keyedLen++;
14120 keyed[key] = _child;
14121 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
14122 }
14123 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
14124 vchild = vchildren[i];
14125 child = null;
14126 var key = vchild.key;
14127 if (null != key) {
14128 if (keyedLen && void 0 !== keyed[key]) {
14129 child = keyed[key];
14130 keyed[key] = void 0;
14131 keyedLen--;
14132 }
14133 } else if (!child && min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
14134 child = c;
14135 children[j] = void 0;
14136 if (j === childrenLen - 1) childrenLen--;
14137 if (j === min) min++;
14138 break;
14139 }
14140 child = idiff(child, vchild, context, mountAll);
14141 f = originalChildren[i];
14142 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
14143 }
14144 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
14145 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
14146 }
14147 function recollectNodeTree(node, unmountOnly) {
14148 var component = node._component;
14149 if (component) unmountComponent(component); else {
14150 if (null != node.__preactattr_ && node.__preactattr_.ref) node.__preactattr_.ref(null);
14151 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
14152 removeChildren(node);
14153 }
14154 }
14155 function removeChildren(node) {
14156 node = node.lastChild;
14157 while (node) {
14158 var next = node.previousSibling;
14159 recollectNodeTree(node, !0);
14160 node = next;
14161 }
14162 }
14163 function diffAttributes(dom, attrs, old) {
14164 var name;
14165 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
14166 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);
14167 }
14168 function collectComponent(component) {
14169 var name = component.constructor.name;
14170 (components[name] || (components[name] = [])).push(component);
14171 }
14172 function createComponent(Ctor, props, context) {
14173 var inst, list = components[Ctor.name];
14174 if (Ctor.prototype && Ctor.prototype.render) {
14175 inst = new Ctor(props, context);
14176 Component.call(inst, props, context);
14177 } else {
14178 inst = new Component(props, context);
14179 inst.constructor = Ctor;
14180 inst.render = doRender;
14181 }
14182 if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) {
14183 inst.__b = list[i].__b;
14184 list.splice(i, 1);
14185 break;
14186 }
14187 return inst;
14188 }
14189 function doRender(props, state, context) {
14190 return this.constructor(props, context);
14191 }
14192 function setComponentProps(component, props, opts, context, mountAll) {
14193 if (!component.__x) {
14194 component.__x = !0;
14195 if (component.__r = props.ref) delete props.ref;
14196 if (component.__k = props.key) delete props.key;
14197 if (!component.base || mountAll) {
14198 if (component.componentWillMount) component.componentWillMount();
14199 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
14200 if (context && context !== component.context) {
14201 if (!component.__c) component.__c = component.context;
14202 component.context = context;
14203 }
14204 if (!component.__p) component.__p = component.props;
14205 component.props = props;
14206 component.__x = !1;
14207 if (0 !== opts) if (1 === opts || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
14208 if (component.__r) component.__r(component);
14209 }
14210 }
14211 function renderComponent(component, opts, mountAll, isChild) {
14212 if (!component.__x) {
14213 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;
14214 if (isUpdate) {
14215 component.props = previousProps;
14216 component.state = previousState;
14217 component.context = previousContext;
14218 if (2 !== opts && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
14219 component.props = props;
14220 component.state = state;
14221 component.context = context;
14222 }
14223 component.__p = component.__s = component.__c = component.__b = null;
14224 component.__d = !1;
14225 if (!skip) {
14226 rendered = component.render(props, state, context);
14227 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
14228 var toUnmount, base, childComponent = rendered && rendered.nodeName;
14229 if ('function' == typeof childComponent) {
14230 var childProps = getNodeProps(rendered);
14231 inst = initialChildComponent;
14232 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
14233 toUnmount = inst;
14234 component._component = inst = createComponent(childComponent, childProps, context);
14235 inst.__b = inst.__b || nextBase;
14236 inst.__u = component;
14237 setComponentProps(inst, childProps, 0, context, !1);
14238 renderComponent(inst, 1, mountAll, !0);
14239 }
14240 base = inst.base;
14241 } else {
14242 cbase = initialBase;
14243 toUnmount = initialChildComponent;
14244 if (toUnmount) cbase = component._component = null;
14245 if (initialBase || 1 === opts) {
14246 if (cbase) cbase._component = null;
14247 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
14248 }
14249 }
14250 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
14251 var baseParent = initialBase.parentNode;
14252 if (baseParent && base !== baseParent) {
14253 baseParent.replaceChild(base, initialBase);
14254 if (!toUnmount) {
14255 initialBase._component = null;
14256 recollectNodeTree(initialBase, !1);
14257 }
14258 }
14259 }
14260 if (toUnmount) unmountComponent(toUnmount);
14261 component.base = base;
14262 if (base && !isChild) {
14263 var componentRef = component, t = component;
14264 while (t = t.__u) (componentRef = t).base = base;
14265 base._component = componentRef;
14266 base._componentConstructor = componentRef.constructor;
14267 }
14268 }
14269 if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) {
14270 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext);
14271 if (options.afterUpdate) options.afterUpdate(component);
14272 }
14273 if (null != component.__h) while (component.__h.length) component.__h.pop().call(component);
14274 if (!diffLevel && !isChild) flushMounts();
14275 }
14276 }
14277 function buildComponentFromVNode(dom, vnode, context, mountAll) {
14278 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
14279 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
14280 if (c && isOwner && (!mountAll || c._component)) {
14281 setComponentProps(c, props, 3, context, mountAll);
14282 dom = c.base;
14283 } else {
14284 if (originalComponent && !isDirectOwner) {
14285 unmountComponent(originalComponent);
14286 dom = oldDom = null;
14287 }
14288 c = createComponent(vnode.nodeName, props, context);
14289 if (dom && !c.__b) {
14290 c.__b = dom;
14291 oldDom = null;
14292 }
14293 setComponentProps(c, props, 1, context, mountAll);
14294 dom = c.base;
14295 if (oldDom && dom !== oldDom) {
14296 oldDom._component = null;
14297 recollectNodeTree(oldDom, !1);
14298 }
14299 }
14300 return dom;
14301 }
14302 function unmountComponent(component) {
14303 if (options.beforeUnmount) options.beforeUnmount(component);
14304 var base = component.base;
14305 component.__x = !0;
14306 if (component.componentWillUnmount) component.componentWillUnmount();
14307 component.base = null;
14308 var inner = component._component;
14309 if (inner) unmountComponent(inner); else if (base) {
14310 if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null);
14311 component.__b = base;
14312 removeNode(base);
14313 collectComponent(component);
14314 removeChildren(base);
14315 }
14316 if (component.__r) component.__r(null);
14317 }
14318 function Component(props, context) {
14319 this.__d = !0;
14320 this.context = context;
14321 this.props = props;
14322 this.state = this.state || {};
14323 }
14324 function render(vnode, parent, merge) {
14325 return diff(merge, vnode, {}, !1, parent, !1);
14326 }
14327 var options = {};
14328 var stack = [];
14329 var EMPTY_CHILDREN = [];
14330 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14331 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14332 var items = [];
14333 var mounts = [];
14334 var diffLevel = 0;
14335 var isSvgMode = !1;
14336 var hydrating = !1;
14337 var components = {};
14338 extend(Component.prototype, {
14339 setState: function(state, callback) {
14340 var s = this.state;
14341 if (!this.__s) this.__s = extend({}, s);
14342 extend(s, 'function' == typeof state ? state(s, this.props) : state);
14343 if (callback) (this.__h = this.__h || []).push(callback);
14344 enqueueRender(this);
14345 },
14346 forceUpdate: function(callback) {
14347 if (callback) (this.__h = this.__h || []).push(callback);
14348 renderComponent(this, 2);
14349 },
14350 render: function() {}
14351 });
14352 var preact = {
14353 h: h,
14354 createElement: h,
14355 cloneElement: cloneElement,
14356 Component: Component,
14357 render: render,
14358 rerender: rerender,
14359 options: options
14360 };
14361 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14362 }();
14363
14364 },{}]},{},[2]);
14365 ; })();