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

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

14,342 lines 543.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined;(function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
2 'use strict';
3
4 Object.defineProperty(exports, "__esModule", {
5 value: true
6 });
7 function init() {
8 document.body.addEventListener('click', handleClickEvent, true);
9 }
10
11 function handleClickEvent(e) {
12 if (e.target.tagName !== 'A') {
13 return;
14 }
15
16 if (e.target.hasAttribute('data-hf-confirm')) {
17 var sure = confirm(e.target.getAttribute('data-hf-confirm'));
18
19 if (!sure) {
20 e.preventDefault();
21 }
22 }
23 }
24
25 exports.default = {
26 init: init
27 };
28
29 },{}],2:[function(require,module,exports){
30 'use strict';
31
32 var _tabs = require('./tabs.js');
33
34 var _tabs2 = _interopRequireDefault(_tabs);
35
36 var _formEditor = require('./form-editor.js');
37
38 var _formEditor2 = _interopRequireDefault(_formEditor);
39
40 var _formActions = require('./form-actions.js');
41
42 var _formActions2 = _interopRequireDefault(_formActions);
43
44 var _fieldBuilder = require('./field-builder.js');
45
46 var _fieldBuilder2 = _interopRequireDefault(_fieldBuilder);
47
48 var _actionConfirmations = require('./action-confirmations.js');
49
50 var _actionConfirmations2 = _interopRequireDefault(_actionConfirmations);
51
52 var _tlite = require('tlite');
53
54 var _tlite2 = _interopRequireDefault(_tlite);
55
56 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
57
58 window.html_forms = {};
59
60 // init the various components
61 _tabs2.default.init();
62 _actionConfirmations2.default.init();
63
64 if (document.getElementById('hf-form-editor')) {
65 _formEditor2.default.init();
66 _formActions2.default.init();
67 _fieldBuilder2.default.init(_formEditor2.default);
68 }
69
70 (0, _tlite2.default)(function (el) {
71 return el.className.indexOf('hf-tooltip') > -1;
72 });
73
74 window.html_forms.FieldBuilder = _fieldBuilder2.default;
75 window.html_forms.Editor = _formEditor2.default;
76
77 },{"./action-confirmations.js":1,"./field-builder.js":6,"./form-actions.js":8,"./form-editor.js":9,"./tabs.js":10,"tlite":23}],3:[function(require,module,exports){
78 'use strict';
79
80 Object.defineProperty(exports, "__esModule", {
81 value: true
82 });
83 exports.FieldBuilder = undefined;
84
85 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
86
87 var _desc, _value, _class;
88
89 var _preact = require('preact');
90
91 var _decko = require('decko');
92
93 var _fieldConfigurator = require('./field-configurator.js');
94
95 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
96
97 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; }
98
99 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; }
100
101 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
102 var desc = {};
103 Object['ke' + 'ys'](descriptor).forEach(function (key) {
104 desc[key] = descriptor[key];
105 });
106 desc.enumerable = !!desc.enumerable;
107 desc.configurable = !!desc.configurable;
108
109 if ('value' in desc || desc.initializer) {
110 desc.writable = true;
111 }
112
113 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
114 return decorator(target, property, desc) || desc;
115 }, desc);
116
117 if (context && desc.initializer !== void 0) {
118 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
119 desc.initializer = undefined;
120 }
121
122 if (desc.initializer === void 0) {
123 Object['define' + 'Property'](target, property, desc);
124 desc = null;
125 }
126
127 return desc;
128 }
129
130 var FieldBuilder = (_class = function (_Component) {
131 _inherits(FieldBuilder, _Component);
132
133 function FieldBuilder(props) {
134 _classCallCheck(this, FieldBuilder);
135
136 var _this = _possibleConstructorReturn(this, (FieldBuilder.__proto__ || Object.getPrototypeOf(FieldBuilder)).call(this, props));
137
138 _this.state = {
139 activeField: null
140 };
141 return _this;
142 }
143
144 _createClass(FieldBuilder, [{
145 key: 'handleCancel',
146 value: function handleCancel() {
147 this.setState({
148 activeField: null
149 });
150 }
151 }, {
152 key: 'openFieldConfig',
153 value: function openFieldConfig(e) {
154 var field = this.props.fields[e.target.value];
155
156 if (this.state.activeField === field) {
157 this.setState({ activeField: null });
158 } else {
159 this.setState({ activeField: field });
160 }
161 }
162 }, {
163 key: 'render',
164 value: function render(props, state) {
165 var _this2 = this;
166
167 var fieldButtons = props.fields.map(function (f, i) {
168 return (0, _preact.h)(
169 'button',
170 { type: 'button', value: i, className: "button " + (state.activeField === f ? "active" : ""), onClick: _this2.openFieldConfig },
171 f.label
172 );
173 });
174 var fieldType = state.activeField ? state.activeField.key : "";
175 var rows = state.activeField ? state.activeField.configRows : [];
176
177 return (0, _preact.h)(
178 'div',
179 { 'class': 'hf-field-builder' },
180 (0, _preact.h)(
181 'h4',
182 null,
183 'Add field'
184 ),
185 (0, _preact.h)(
186 'div',
187 { 'class': 'available-fields' },
188 fieldButtons
189 ),
190 (0, _preact.h)(
191 'div',
192 { style: 'max-width: 480px;' },
193 (0, _preact.h)(_fieldConfigurator.FieldConfigurator, { fieldType: fieldType, rows: rows, onCancel: this.handleCancel })
194 ),
195 state.activeField === null ? (0, _preact.h)(
196 'p',
197 { 'class': 'help', style: 'margin-bottom: 0;' },
198 'Use the buttons above to generate your field HTML, or manually modify your form below.'
199 ) : ""
200 );
201 }
202 }]);
203
204 return FieldBuilder;
205 }(_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);
206 exports.FieldBuilder = FieldBuilder;
207
208 },{"./field-configurator.js":4,"decko":19,"preact":22}],4:[function(require,module,exports){
209 'use strict';
210
211 Object.defineProperty(exports, "__esModule", {
212 value: true
213 });
214 exports.FieldConfigurator = undefined;
215
216 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; }; }();
217
218 var _desc, _value, _class;
219
220 var _preact = require('preact');
221
222 var _decko = require('decko');
223
224 var _html = require('../field-builder/html.js');
225
226 var _fieldSettings = require('./field-settings.js');
227
228 var FS = _interopRequireWildcard(_fieldSettings);
229
230 var _linkstate = require('linkstate');
231
232 var _linkstate2 = _interopRequireDefault(_linkstate);
233
234 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
235
236 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
237
238 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
239
240 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; }
241
242 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; }
243
244 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
245 var desc = {};
246 Object['ke' + 'ys'](descriptor).forEach(function (key) {
247 desc[key] = descriptor[key];
248 });
249 desc.enumerable = !!desc.enumerable;
250 desc.configurable = !!desc.configurable;
251
252 if ('value' in desc || desc.initializer) {
253 desc.writable = true;
254 }
255
256 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
257 return decorator(target, property, desc) || desc;
258 }, desc);
259
260 if (context && desc.initializer !== void 0) {
261 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
262 desc.initializer = undefined;
263 }
264
265 if (desc.initializer === void 0) {
266 Object['define' + 'Property'](target, property, desc);
267 desc = null;
268 }
269
270 return desc;
271 }
272
273 var FieldConfigurator = (_class = function (_Component) {
274 _inherits(FieldConfigurator, _Component);
275
276 function FieldConfigurator(props) {
277 _classCallCheck(this, FieldConfigurator);
278
279 var _this = _possibleConstructorReturn(this, (FieldConfigurator.__proto__ || Object.getPrototypeOf(FieldConfigurator)).call(this, props));
280
281 _this.state = _this.getInitialState();
282 _this.choiceHandlers = {
283 "add": _this.addChoice,
284 "delete": _this.deleteChoice,
285 "changeLabel": _this.changeChoiceLabel,
286 "toggleChecked": _this.toggleChoiceChecked
287 };
288 return _this;
289 }
290
291 _createClass(FieldConfigurator, [{
292 key: 'getInitialState',
293 value: function getInitialState() {
294 return {
295 fieldType: "",
296 fieldLabel: "",
297 placeholder: "",
298 value: "",
299 wrap: true,
300 required: false,
301 choices: [{
302 checked: false,
303 label: "One"
304 }, {
305 checked: false,
306 label: "Two"
307 }],
308 accept: ''
309 };
310 }
311 }, {
312 key: 'componentWillReceiveProps',
313 value: function componentWillReceiveProps(props) {
314 this.setState({ fieldType: props.fieldType });
315 }
316 }, {
317 key: 'addToForm',
318 value: function addToForm() {
319 var html = (0, _html.htmlgenerate)(this.state);
320 html_forms.Editor.replaceSelection(html);
321 }
322 }, {
323 key: 'addChoice',
324 value: function addChoice() {
325 var arr = this.state.choices;
326 arr.push({ checked: false, label: "..." });
327 this.setState({ choices: arr });
328 }
329 }, {
330 key: 'deleteChoice',
331 value: function deleteChoice(e) {
332 var arr = this.state.choices;
333 var index = e.target.parentElement.getAttribute('data-key');
334 arr.splice(index, 1);
335 this.setState({ choices: arr });
336 }
337 }, {
338 key: 'changeChoiceLabel',
339 value: function changeChoiceLabel(e) {
340 var arr = this.state.choices;
341 var index = e.target.parentElement.getAttribute('data-key');
342 arr[index].label = e.target.value;
343 this.setState({ choices: arr });
344 }
345 }, {
346 key: 'toggleChoiceChecked',
347 value: function toggleChoiceChecked(e) {
348 var arr = this.state.choices;
349 var index = e.target.parentElement.getAttribute('data-key');
350 arr[index].checked = !arr[index].checked;
351 this.setState({ choices: arr });
352 }
353 }, {
354 key: 'handleCancel',
355 value: function handleCancel() {
356 // revert back to initial state
357 this.setState(this.getInitialState());
358 this.props.onCancel();
359 }
360 }, {
361 key: 'render',
362 value: function render(props, state) {
363 if (props.rows.length == 0) {
364 return "";
365 }
366
367 var formFields = [];
368
369 for (var i = 0; i < props.rows.length; i++) {
370 switch (props.rows[i]) {
371 case "label":
372 formFields.push((0, _preact.h)(FS.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }));
373 break;
374
375 case "placeholder":
376 formFields.push((0, _preact.h)(FS.Placeholder, { value: state.placeholder, onChange: (0, _linkstate2.default)(this, 'placeholder') }));
377 break;
378
379 case "default-value":
380 formFields.push((0, _preact.h)(FS.DefaultValue, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }));
381 break;
382
383 case "required":
384 formFields.push((0, _preact.h)(FS.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }));
385 break;
386
387 case "wrap":
388 formFields.push((0, _preact.h)(FS.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }));
389 break;
390
391 case "add-to-form":
392 formFields.push((0, _preact.h)(FS.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel }));
393 break;
394
395 case "choices":
396 formFields.push((0, _preact.h)(FS.Choices, { multiple: false, choices: state.choices, handlers: this.choiceHandlers }));
397 break;
398
399 case "button-text":
400 formFields.push((0, _preact.h)(FS.ButtonText, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }));
401 break;
402
403 case "accept":
404 formFields.push((0, _preact.h)(FS.Accept, { value: state.accept, onChange: (0, _linkstate2.default)(this, 'accept') }));
405 break;
406
407 }
408 }
409
410 return (0, _preact.h)(
411 'div',
412 { 'class': 'field-config', onKeyPress: FieldConfigurator.handleKeyPress },
413 formFields
414 );
415 }
416 }], [{
417 key: 'handleKeyPress',
418 value: function handleKeyPress(e) {
419 // stop RETURN from submitting the parent form.
420 if (e.keyCode === 13) {
421 e.preventDefault();
422 }
423 }
424 }]);
425
426 return FieldConfigurator;
427 }(_preact.Component), (_applyDecoratedDescriptor(_class.prototype, 'addToForm', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'addToForm'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'addChoice', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'addChoice'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'deleteChoice', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'deleteChoice'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'changeChoiceLabel', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'changeChoiceLabel'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'toggleChoiceChecked', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'toggleChoiceChecked'), _class.prototype), _applyDecoratedDescriptor(_class, 'handleKeyPress', [_decko.bind], Object.getOwnPropertyDescriptor(_class, 'handleKeyPress'), _class), _applyDecoratedDescriptor(_class.prototype, 'handleCancel', [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, 'handleCancel'), _class.prototype)), _class);
428 exports.FieldConfigurator = FieldConfigurator;
429
430 },{"../field-builder/html.js":7,"./field-settings.js":5,"decko":19,"linkstate":20,"preact":22}],5:[function(require,module,exports){
431 'use strict';
432
433 Object.defineProperty(exports, "__esModule", {
434 value: true
435 });
436 exports.Accept = exports.ButtonText = exports.Choices = exports.Required = exports.Wrap = exports.DefaultValue = exports.Placeholder = exports.Label = exports.AddToForm = undefined;
437
438 var _preact = require('preact');
439
440 function AddToForm(props) {
441 return (0, _preact.h)(
442 'div',
443 { 'class': 'hf-small-margin' },
444 (0, _preact.h)(
445 'button',
446 { 'class': 'button', type: 'button', onClick: props.onSubmit },
447 'Add field to form'
448 ),
449 ' \xA0 ',
450 (0, _preact.h)(
451 'a',
452 { href: 'javascript:void(0);', 'class': 'hf-small', style: 'vertical-align: middle;', onClick: props.onCancel },
453 'or close field helper'
454 )
455 );
456 }
457
458 function Label(props) {
459 return (0, _preact.h)(
460 'div',
461 { 'class': 'hf-small-margin' },
462 (0, _preact.h)(
463 'label',
464 { 'for': 'hf-fg-field-label' },
465 'Field label ',
466 (0, _preact.h)(
467 'span',
468 { 'class': 'hf-required' },
469 '*'
470 )
471 ),
472 (0, _preact.h)('input', { id: 'hf-fg-field-label', type: 'text', value: props.value, onChange: props.onChange })
473 );
474 }
475
476 function Placeholder(props) {
477 return (0, _preact.h)(
478 'div',
479 { 'class': 'hf-small-margin' },
480 (0, _preact.h)(
481 'label',
482 { 'for': 'hf-fg-placeholder' },
483 'Placeholder ',
484 (0, _preact.h)(
485 'span',
486 { 'class': 'hf-italic hf-pull-right' },
487 'Optional'
488 )
489 ),
490 (0, _preact.h)('input', { id: 'hf-fg-placeholder', type: 'text', value: props.value, onChange: props.onChange }),
491 (0, _preact.h)(
492 'p',
493 { 'class': 'help' },
494 'Text to show when field has no value.'
495 )
496 );
497 }
498
499 function ButtonText(props) {
500 return (0, _preact.h)(
501 'div',
502 { 'class': 'hf-small-margin' },
503 (0, _preact.h)(
504 'label',
505 { 'for': 'hf-fg-default-value' },
506 'Button text ',
507 (0, _preact.h)(
508 'span',
509 { 'class': 'hf-required' },
510 '*'
511 )
512 ),
513 (0, _preact.h)('input', { id: 'hf-fg-default-value', type: 'text', value: props.value, onChange: props.onChange }),
514 (0, _preact.h)(
515 'p',
516 { 'class': 'help' },
517 'Text to show on the button.'
518 )
519 );
520 }
521
522 function DefaultValue(props) {
523 return (0, _preact.h)(
524 'div',
525 { 'class': 'hf-small-margin' },
526 (0, _preact.h)(
527 'label',
528 { 'for': 'hf-fg-default-value' },
529 'Default value ',
530 (0, _preact.h)(
531 'span',
532 { 'class': 'hf-italic hf-pull-right' },
533 'Optional'
534 )
535 ),
536 (0, _preact.h)('input', { id: 'hf-fg-default-value', type: 'text', value: props.value, onChange: props.onChange }),
537 (0, _preact.h)(
538 'p',
539 { 'class': 'help' },
540 'Text to pre-fill this field with.'
541 )
542 );
543 }
544
545 function Wrap(props) {
546 return (0, _preact.h)(
547 'div',
548 { 'class': 'hf-small-margin' },
549 (0, _preact.h)(
550 'label',
551 { 'class': 'inline' },
552 (0, _preact.h)('input', { type: 'checkbox', value: '1', defaultChecked: props.checked, onChange: props.onChange }),
553 'Wrap this field in paragraph tags.'
554 )
555 );
556 }
557
558 function Required(props) {
559 return (0, _preact.h)(
560 'div',
561 { 'class': 'hf-small-margin' },
562 (0, _preact.h)(
563 'label',
564 { 'class': 'inline' },
565 (0, _preact.h)('input', { type: 'checkbox', value: '1', defaultChecked: props.checked, onChange: props.onChange }),
566 'This field is required.'
567 )
568 );
569 }
570
571 function Choices(props) {
572 var choiceFields = props.choices.map(function (choice, k) {
573 return (0, _preact.h)(
574 'div',
575 { 'data-key': k },
576 (0, _preact.h)('input', { type: props.multiple ? "checkbox" : "radio", name: 'selected', defaultChecked: choice.checked, onChange: props.handlers.toggleChecked, title: 'Pre-select this choice?' }),
577 (0, _preact.h)('input', { type: 'text', value: choice.label, placeholder: 'Choice label', style: 'width: 80%;', onChange: props.handlers.changeLabel }),
578 (0, _preact.h)(
579 'a',
580 { href: 'javascript:void(0);', onClick: props.handlers.delete, style: 'text-decoration: none;', title: 'Delete choice' },
581 '\u2715'
582 )
583 );
584 });
585
586 return (0, _preact.h)(
587 'div',
588 { 'class': 'hf-small-margin' },
589 (0, _preact.h)(
590 'label',
591 null,
592 'Choices'
593 ),
594 choiceFields,
595 (0, _preact.h)('input', { type: props.multiple ? "checkbox" : "radio", style: 'visibility: hidden;' }),
596 (0, _preact.h)(
597 'a',
598 { href: 'javascript:void(0);', onClick: props.handlers.add },
599 'Add choice'
600 )
601 );
602 }
603
604 function Accept(props) {
605 return (0, _preact.h)(
606 'div',
607 { 'class': 'hf-small-margin' },
608 (0, _preact.h)(
609 'label',
610 null,
611 'Accepted file types'
612 ),
613 (0, _preact.h)('input', { type: 'text', value: props.value, onChange: props.onChange }),
614 (0, _preact.h)(
615 'p',
616 { 'class': 'help' },
617 'Use a comma-separated list of accepted file extensions, eg ',
618 (0, _preact.h)(
619 'code',
620 null,
621 '.pdf'
622 ),
623 '. ',
624 (0, _preact.h)('br', null),
625 'Leave empty to accept any file type.'
626 )
627 );
628 }
629
630 exports.AddToForm = AddToForm;
631 exports.Label = Label;
632 exports.Placeholder = Placeholder;
633 exports.DefaultValue = DefaultValue;
634 exports.Wrap = Wrap;
635 exports.Required = Required;
636 exports.Choices = Choices;
637 exports.ButtonText = ButtonText;
638 exports.Accept = Accept;
639
640 },{"preact":22}],6:[function(require,module,exports){
641 'use strict';
642
643 // imports
644
645 Object.defineProperty(exports, "__esModule", {
646 value: true
647 });
648
649 var _preact = require('preact');
650
651 var _fieldBuilder = require('./components/field-builder.js');
652
653 // vars
654 var rootElement = void 0;
655 var Editor = void 0;
656 var fields = void 0;
657
658 // functions
659 function Field(key, label, configRows) {
660 this.key = key;
661 this.label = label;
662 this.configRows = configRows || [];
663 }
664
665 function mount() {
666 rootElement = (0, _preact.render)((0, _preact.h)(_fieldBuilder.FieldBuilder, { fields: fields }), document.getElementById('hf-field-builder'), rootElement);
667 }
668
669 // bootstrap
670 fields = [new Field("text", "Text", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("email", "Email", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("url", "URL", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("number", "Number", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("date", "Date", ["label", "default-value", "required", "wrap", "add-to-form"]), new Field("textarea", "Textarea", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("dropdown", "Dropdown", ["label", "choices", "required", "wrap", "add-to-form"]), new Field("checkboxes", "Checkboxes", ["label", "choices", "wrap", "add-to-form"]), new Field("radio-buttons", "Radio buttons", ["label", "choices", "wrap", "add-to-form"]), new Field("submit", "Submit button", ["button-text", "wrap", "add-to-form"])];
671
672 exports.default = {
673 init: function init() {
674 mount();
675 },
676
677 registerField: function registerField(key, label, configRows) {
678 fields.push(new Field(key, label, configRows));
679 mount();
680 }
681 };
682
683 },{"./components/field-builder.js":3,"preact":22}],7:[function(require,module,exports){
684 'use strict';
685
686 Object.defineProperty(exports, "__esModule", {
687 value: true
688 });
689 exports.htmlgenerate = undefined;
690
691 var _preactRenderToString = require('preact-render-to-string');
692
693 var _preactRenderToString2 = _interopRequireDefault(_preactRenderToString);
694
695 var _preact = require('preact');
696
697 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
698
699 function htmlgenerate(conf) {
700 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)("label", {}, conf.fieldLabel) : "";
701 var fieldAttr = void 0,
702 field = void 0;
703
704 switch (conf.fieldType) {
705 case "text":
706 default:
707 fieldAttr = {
708 type: conf.fieldType,
709 name: namify(conf.fieldLabel),
710 value: conf.value,
711 placeholder: conf.placeholder,
712 required: conf.required
713 };
714 field = html("input", fieldAttr);
715 break;
716 case "textarea":
717 fieldAttr = {
718 name: namify(conf.fieldLabel),
719 placeholder: conf.placeholder,
720 required: conf.required
721 };
722 field = html("textarea", fieldAttr, conf.value);
723 break;
724
725 case "dropdown":
726 fieldAttr = {
727 name: namify(conf.fieldLabel),
728 required: conf.required
729 };
730 var opts = conf.choices.map(function (choice) {
731 return html("option", { selected: choice.checked }, choice.label);
732 });
733 field = html("select", fieldAttr, opts);
734 break;
735
736 case "radio-buttons":
737 field = conf.choices.map(function (choice) {
738 return html("label", {}, [html("input", {
739 type: "radio",
740 name: namify(conf.fieldLabel),
741 value: choice.label,
742 selected: choice.checked
743 }), " ", html("span", {}, choice.label)]);
744 });
745 break;
746
747 case "checkboxes":
748 field = conf.choices.map(function (choice) {
749 return html("label", {}, [html("input", {
750 type: "checkbox",
751 name: namify(conf.fieldLabel) + "[]",
752 value: choice.label,
753 checked: choice.checked
754 }), " ", html("span", {}, choice.label)]);
755 });
756 break;
757
758 case "file":
759 fieldAttr = {
760 type: "file",
761 name: namify(conf.fieldLabel),
762 required: conf.required
763 };
764
765 if (conf['accept']) {
766 fieldAttr['accept'] = conf['accept'];
767 }
768
769 field = html("input", fieldAttr);
770 break;
771
772 case "submit":
773 fieldAttr = {
774 type: "submit",
775 value: conf.value
776 };
777 field = html("input", fieldAttr);
778 break;
779
780 }
781
782 var str = "";
783 if (conf.wrap) {
784 var tmpl = (0, _preact.h)("p", {}, [label, field]);
785 str = (0, _preactRenderToString2.default)(tmpl, null, { pretty: true });
786 } else {
787 str += (0, _preactRenderToString2.default)(label, null, { pretty: true });
788 str += "\n";
789 str += (0, _preactRenderToString2.default)(field, null, { pretty: true });
790 }
791
792 return str;
793 }
794
795 function html(tag, attr, children) {
796 return (0, _preact.h)(tag, filterEmptyObjectValues(attr), children);
797 }
798
799 function namify(str) {
800 return str.replace(/ /g, '_').replace(/[^\w\[\]_]*/g, "").toUpperCase();
801 }
802
803 function filterEmptyObjectValues(obj) {
804 var newObj = {};
805 for (var propName in obj) {
806 if (obj[propName] !== false && obj[propName] !== "") {
807 newObj[propName] = obj[propName];
808 }
809 }
810 return newObj;
811 }
812
813 exports.htmlgenerate = htmlgenerate;
814
815 },{"preact":22,"preact-render-to-string":21}],8:[function(require,module,exports){
816 'use strict';
817
818 Object.defineProperty(exports, "__esModule", {
819 value: true
820 });
821 var availableActions = void 0,
822 actionTemplates = void 0,
823 actions = void 0;
824
825 function init() {
826 actions = document.getElementById('hf-form-actions');
827 availableActions = document.getElementById('hf-available-form-actions');
828 actionTemplates = document.getElementById('hf-form-action-templates');
829
830 // turn settings into accordions
831 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
832 el.parentNode.removeChild(el);
833
834 var heading = el.getAttribute('data-title');
835 var summary = el.querySelector('.hf-action-summary');
836 if (summary) {
837 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
838 }
839 var wrap = createAccordion(heading, el.innerHTML);
840 actions.appendChild(wrap);
841
842 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
843 });
844
845 availableActions.addEventListener('click', addAction, true);
846 }
847
848 function createAccordion(headingHTML, contentHTML) {
849 var wrap = document.createElement('div');
850 wrap.className = "hf-accordion expanded ";
851
852 var heading = document.createElement('h4');
853 heading.className = "hf-accordion-heading";
854 heading.innerHTML = headingHTML;
855 wrap.appendChild(heading);
856
857 var content = document.createElement('div');
858 content.className = "hf-accordion-content";
859 content.innerHTML = contentHTML;
860 wrap.appendChild(content);
861
862 var deleteWrap = document.createElement('p');
863 deleteWrap.style.textAlign = 'right';
864 var deleteLink = document.createElement('a');
865 deleteLink.href = 'javascript:void(0);';
866 deleteLink.className = "danger";
867 deleteLink.innerText = 'Delete this action';
868 deleteWrap.appendChild(deleteLink);
869 content.appendChild(deleteWrap);
870
871 // bind handlers
872 heading.addEventListener('click', createToggleActionHandler(wrap, content));
873 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
874 return wrap;
875 }
876
877 function addAction(e) {
878 var el = e.target || e.srcElement;
879 if (el.tagName !== 'INPUT') {
880 return;
881 }
882
883 var actionType = el.getAttribute('data-action-type');
884 var actionTemplate = actionTemplates.querySelector('#hf-action-type-' + actionType + '-template');
885
886 // append HTML to actions wrapper
887 var index = actions.querySelectorAll('div').length - 1;
888 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
889 actions.appendChild(wrap);
890
891 // hide "no form actions" message
892 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
893 }
894
895 function createDeleteActionHandler(wrap) {
896 return function () {
897 actions.removeChild(wrap);
898
899 if (actions.childElementCount === 1) {
900 actions.querySelector('#hf-form-actions-empty').style.display = '';
901 }
902 };
903 }
904
905 function createToggleActionHandler(wrap, content) {
906 return function () {
907 var show = content.offsetParent === null;
908 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
909 content.style.display = show ? 'block' : 'none';
910 };
911 }
912
913 exports.default = { init: init };
914
915 },{}],9:[function(require,module,exports){
916 'use strict';
917
918 // load CodeMirror & plugins
919
920 Object.defineProperty(exports, "__esModule", {
921 value: true
922 });
923 var CodeMirror = require('codemirror');
924 require('codemirror/mode/xml/xml');
925 require('codemirror/mode/javascript/javascript');
926 require('codemirror/mode/css/css');
927 require('codemirror/mode/htmlmixed/htmlmixed');
928 require('codemirror/addon/fold/xml-fold');
929 require('codemirror/addon/edit/matchtags');
930 require('codemirror/addon/edit/closetag.js');
931
932 var editor = void 0,
933 element = void 0,
934 dom = void 0,
935 requiredFieldsInput = void 0,
936 emailFieldsInput = void 0,
937 previewFrame = void 0,
938 previewDom = void 0;
939 var templateRegex = /\{\{ *(\w+)(?:\.([\w\.]+))? *(?:\|\| *(\w+))? *\}\}/g;
940
941 function init() {
942 previewFrame = document.getElementById('hf-form-preview');
943 element = document.getElementById('hf-form-editor');
944 dom = document.createElement('form');
945 requiredFieldsInput = document.getElementById('hf-required-fields');
946 emailFieldsInput = document.getElementById('hf-email-fields');
947
948 dom.innerHTML = element.value;
949 editor = CodeMirror.fromTextArea(element, {
950 selectionPointer: true,
951 matchTags: { bothTags: true },
952 mode: "htmlmixed",
953 htmlMode: true,
954 autoCloseTags: true,
955 autoRefresh: true,
956 styleActiveLine: true,
957 matchBrackets: true
958 });
959
960 editor.on('changes', debounce(updatePreview, 500));
961 editor.on('changes', debounce(updateShadowDOM, 100));
962 editor.on('changes', debounce(updateFieldVariables, 500));
963 editor.on('blur', updatePreview);
964 editor.on('blur', updateShadowDOM);
965 editor.on('blur', updateFieldVariables);
966 editor.on('blur', updateRequiredFields);
967 editor.on('blur', updateEmailFields);
968
969 previewFrame.addEventListener('load', setPreviewDom);
970 setPreviewDom();
971 updateFieldVariables();
972 }
973
974 function setPreviewDom() {
975 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
976 previewDom = frameContent.querySelector('.hf-fields-wrap');
977
978 if (previewDom) {
979 updatePreview();
980 }
981 }
982
983 function getFieldVariableName(f) {
984 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
985 }
986
987 function updateFieldVariables() {
988 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
989 var fieldVariables = uniq([].map.call(fields, function (f) {
990 return '[' + getFieldVariableName(f) + ']';
991 }));
992 var wpbody = document.getElementById('wpbody-content');
993
994 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
995 // remove existing variables
996 while (el.firstChild) {
997 el.removeChild(el.firstChild);
998 }
999
1000 var variableElements = fieldVariables.map(function (n) {
1001 // measure width of actual font size for prettiness
1002 var sizeEl = document.createElement('span');
1003 sizeEl.style.visibility = 'hidden';
1004 sizeEl.innerText = n;
1005 wpbody.appendChild(sizeEl);
1006 var width = sizeEl.offsetWidth;
1007 wpbody.removeChild(sizeEl);
1008
1009 // add input el
1010 var el = document.createElement('input');
1011 el.setAttribute('type', 'text');
1012 el.style.maxWidth = width * 1.1 + 14 + 'px';
1013 el.setAttribute('value', n);
1014 el.setAttribute('readonly', true);
1015 el.setAttribute('onfocus', 'this.select()');
1016 return el;
1017 });
1018
1019 variableElements.forEach(function (vel, i, arr) {
1020 el.appendChild(vel);
1021 });
1022 });
1023 }
1024
1025 function updatePreview() {
1026 var markup = editor.getValue();
1027
1028 // replace template tags
1029 markup = markup.replace(templateRegex, function (s, m) {
1030 if (arguments[3]) {
1031 return arguments[3];
1032 }
1033
1034 return '';
1035 });
1036
1037 // update dom
1038 previewDom.innerHTML = markup;
1039 previewDom.dispatchEvent(new Event('hf-refresh'));
1040 }
1041
1042 function updateShadowDOM() {
1043 dom.innerHTML = editor.getValue();
1044 }
1045
1046 function updateRequiredFields() {
1047 var fields = dom.querySelectorAll('[required]');
1048 var fieldNames = [].map.call(fields, getFieldVariableName);
1049 requiredFieldsInput.value = fieldNames.join(',');
1050 }
1051
1052 function updateEmailFields() {
1053 var fields = dom.querySelectorAll('input[type="email"]');
1054 var fieldNames = [].map.call(fields, getFieldVariableName);
1055 emailFieldsInput.value = fieldNames.join(',');
1056 }
1057
1058 function replaceSelection(str) {
1059 editor.replaceSelection(str);
1060 editor.focus();
1061 }
1062
1063 function debounce(func, wait, immediate) {
1064 var timeout;
1065 return function () {
1066 var context = this,
1067 args = arguments;
1068 var later = function later() {
1069 timeout = null;
1070 if (!immediate) func.apply(context, args);
1071 };
1072 var callNow = immediate && !timeout;
1073 clearTimeout(timeout);
1074 timeout = setTimeout(later, wait);
1075 if (callNow) func.apply(context, args);
1076 };
1077 };
1078
1079 function uniq(a) {
1080 var seen = {};
1081 return a.filter(function (item) {
1082 return seen.hasOwnProperty(item) ? false : seen[item] = true;
1083 });
1084 }
1085
1086 exports.default = {
1087 init: init,
1088 replaceSelection: replaceSelection
1089 };
1090
1091 },{"codemirror":14,"codemirror/addon/edit/closetag.js":11,"codemirror/addon/edit/matchtags":12,"codemirror/addon/fold/xml-fold":13,"codemirror/mode/css/css":15,"codemirror/mode/htmlmixed/htmlmixed":16,"codemirror/mode/javascript/javascript":17,"codemirror/mode/xml/xml":18}],10:[function(require,module,exports){
1092 'use strict';
1093
1094 Object.defineProperty(exports, "__esModule", {
1095 value: true
1096 });
1097 var tabs = void 0,
1098 tabNavs = void 0;
1099
1100 function init() {
1101 tabs = document.querySelectorAll('.hf-tab');
1102 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1103 for (var i = 0; i < tabNavs.length; i++) {
1104 tabNavs[i].addEventListener('click', open);
1105 }
1106 };
1107
1108 function open(e) {
1109 var tabTarget = this.getAttribute('data-tab-target');
1110 for (var i = 0; i < tabNavs.length; i++) {
1111 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1112 }
1113 this.blur();
1114
1115 for (var _i = 0; _i < tabs.length; _i++) {
1116 var tab = tabs[_i];
1117 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1118 }
1119
1120 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + " ");
1121
1122 if (window.history) {
1123 var newUrl = window.location.href;
1124 newUrl = newUrl.replace(/\&tab\=\w+/, "");
1125 newUrl += "&tab=" + tabTarget;
1126
1127 window.history.replaceState({ tab: tabTarget }, document.title, newUrl);
1128 }
1129
1130 e.preventDefault();
1131 };
1132
1133 exports.default = {
1134 init: init,
1135 open: open
1136 };
1137
1138 },{}],11:[function(require,module,exports){
1139 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1140 // Distributed under an MIT license: http://codemirror.net/LICENSE
1141
1142 /**
1143 * Tag-closer extension for CodeMirror.
1144 *
1145 * This extension adds an "autoCloseTags" option that can be set to
1146 * either true to get the default behavior, or an object to further
1147 * configure its behavior.
1148 *
1149 * These are supported options:
1150 *
1151 * `whenClosing` (default true)
1152 * Whether to autoclose when the '/' of a closing tag is typed.
1153 * `whenOpening` (default true)
1154 * Whether to autoclose the tag when the final '>' of an opening
1155 * tag is typed.
1156 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1157 * An array of tag names that should not be autoclosed.
1158 * `indentTags` (default is block tags for HTML, none for XML)
1159 * An array of tag names that should, when opened, cause a
1160 * blank line to be added inside the tag, and the blank line and
1161 * closing line to be indented.
1162 *
1163 * See demos/closetag.html for a usage example.
1164 */
1165
1166 (function(mod) {
1167 if (typeof exports == "object" && typeof module == "object") // CommonJS
1168 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1169 else if (typeof define == "function" && define.amd) // AMD
1170 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1171 else // Plain browser env
1172 mod(CodeMirror);
1173 })(function(CodeMirror) {
1174 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1175 if (old != CodeMirror.Init && old)
1176 cm.removeKeyMap("autoCloseTags");
1177 if (!val) return;
1178 var map = {name: "autoCloseTags"};
1179 if (typeof val != "object" || val.whenClosing)
1180 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1181 if (typeof val != "object" || val.whenOpening)
1182 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1183 cm.addKeyMap(map);
1184 });
1185
1186 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1187 "source", "track", "wbr"];
1188 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1189 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1190
1191 function autoCloseGT(cm) {
1192 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1193 var ranges = cm.listSelections(), replacements = [];
1194 var opt = cm.getOption("autoCloseTags");
1195 for (var i = 0; i < ranges.length; i++) {
1196 if (!ranges[i].empty()) return CodeMirror.Pass;
1197 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1198 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1199 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1200
1201 var html = inner.mode.configuration == "html";
1202 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1203 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1204
1205 var tagName = state.tagName;
1206 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1207 var lowerTagName = tagName.toLowerCase();
1208 // Don't process the '>' at the end of an end-tag or self-closing tag
1209 if (!tagName ||
1210 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1211 tok.type == "tag" && state.type == "closeTag" ||
1212 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1213 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1214 closingTagExists(cm, tagName, pos, state, true))
1215 return CodeMirror.Pass;
1216
1217 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1218 replacements[i] = {indent: indent,
1219 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1220 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1221 }
1222
1223 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1224 for (var i = ranges.length - 1; i >= 0; i--) {
1225 var info = replacements[i];
1226 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1227 var sel = cm.listSelections().slice(0);
1228 sel[i] = {head: info.newPos, anchor: info.newPos};
1229 cm.setSelections(sel);
1230 if (!dontIndentOnAutoClose && info.indent) {
1231 cm.indentLine(info.newPos.line, null, true);
1232 cm.indentLine(info.newPos.line + 1, null, true);
1233 }
1234 }
1235 }
1236
1237 function autoCloseCurrent(cm, typingSlash) {
1238 var ranges = cm.listSelections(), replacements = [];
1239 var head = typingSlash ? "/" : "</";
1240 var opt = cm.getOption("autoCloseTags");
1241 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1242 for (var i = 0; i < ranges.length; i++) {
1243 if (!ranges[i].empty()) return CodeMirror.Pass;
1244 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1245 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1246 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1247 tok.start != pos.ch - 1))
1248 return CodeMirror.Pass;
1249 // Kludge to get around the fact that we are not in XML mode
1250 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1251 // work for other XML embedded languages (there is no general
1252 // way to go from a mixed mode to its current XML state).
1253 var replacement;
1254 if (inner.mode.name != "xml") {
1255 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1256 replacement = head + "script";
1257 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1258 replacement = head + "style";
1259 else
1260 return CodeMirror.Pass;
1261 } else {
1262 if (!state.context || !state.context.tagName ||
1263 closingTagExists(cm, state.context.tagName, pos, state))
1264 return CodeMirror.Pass;
1265 replacement = head + state.context.tagName;
1266 }
1267 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1268 replacements[i] = replacement;
1269 }
1270 cm.replaceSelections(replacements);
1271 ranges = cm.listSelections();
1272 if (!dontIndentOnAutoClose) {
1273 for (var i = 0; i < ranges.length; i++)
1274 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1275 cm.indentLine(ranges[i].head.line);
1276 }
1277 }
1278
1279 function autoCloseSlash(cm) {
1280 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1281 return autoCloseCurrent(cm, true);
1282 }
1283
1284 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1285
1286 function indexOf(collection, elt) {
1287 if (collection.indexOf) return collection.indexOf(elt);
1288 for (var i = 0, e = collection.length; i < e; ++i)
1289 if (collection[i] == elt) return i;
1290 return -1;
1291 }
1292
1293 // If xml-fold is loaded, we use its functionality to try and verify
1294 // whether a given tag is actually unclosed.
1295 function closingTagExists(cm, tagName, pos, state, newTag) {
1296 if (!CodeMirror.scanForClosingTag) return false;
1297 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1298 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1299 if (!nextClose || nextClose.tag != tagName) return false;
1300 var cx = state.context;
1301 // If the immediate wrapping context contains onCx instances of
1302 // the same tag, a closing tag only exists if there are at least
1303 // that many closing tags of that type following.
1304 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1305 pos = nextClose.to;
1306 for (var i = 1; i < onCx; i++) {
1307 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1308 if (!next || next.tag != tagName) return false;
1309 pos = next.to;
1310 }
1311 return true;
1312 }
1313 });
1314
1315 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],12:[function(require,module,exports){
1316 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1317 // Distributed under an MIT license: http://codemirror.net/LICENSE
1318
1319 (function(mod) {
1320 if (typeof exports == "object" && typeof module == "object") // CommonJS
1321 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1322 else if (typeof define == "function" && define.amd) // AMD
1323 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1324 else // Plain browser env
1325 mod(CodeMirror);
1326 })(function(CodeMirror) {
1327 "use strict";
1328
1329 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1330 if (old && old != CodeMirror.Init) {
1331 cm.off("cursorActivity", doMatchTags);
1332 cm.off("viewportChange", maybeUpdateMatch);
1333 clear(cm);
1334 }
1335 if (val) {
1336 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1337 cm.on("cursorActivity", doMatchTags);
1338 cm.on("viewportChange", maybeUpdateMatch);
1339 doMatchTags(cm);
1340 }
1341 });
1342
1343 function clear(cm) {
1344 if (cm.state.tagHit) cm.state.tagHit.clear();
1345 if (cm.state.tagOther) cm.state.tagOther.clear();
1346 cm.state.tagHit = cm.state.tagOther = null;
1347 }
1348
1349 function doMatchTags(cm) {
1350 cm.state.failedTagMatch = false;
1351 cm.operation(function() {
1352 clear(cm);
1353 if (cm.somethingSelected()) return;
1354 var cur = cm.getCursor(), range = cm.getViewport();
1355 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1356 var match = CodeMirror.findMatchingTag(cm, cur, range);
1357 if (!match) return;
1358 if (cm.state.matchBothTags) {
1359 var hit = match.at == "open" ? match.open : match.close;
1360 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1361 }
1362 var other = match.at == "close" ? match.open : match.close;
1363 if (other)
1364 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1365 else
1366 cm.state.failedTagMatch = true;
1367 });
1368 }
1369
1370 function maybeUpdateMatch(cm) {
1371 if (cm.state.failedTagMatch) doMatchTags(cm);
1372 }
1373
1374 CodeMirror.commands.toMatchingTag = function(cm) {
1375 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1376 if (found) {
1377 var other = found.at == "close" ? found.open : found.close;
1378 if (other) cm.extendSelection(other.to, other.from);
1379 }
1380 };
1381 });
1382
1383 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],13:[function(require,module,exports){
1384 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1385 // Distributed under an MIT license: http://codemirror.net/LICENSE
1386
1387 (function(mod) {
1388 if (typeof exports == "object" && typeof module == "object") // CommonJS
1389 mod(require("../../lib/codemirror"));
1390 else if (typeof define == "function" && define.amd) // AMD
1391 define(["../../lib/codemirror"], mod);
1392 else // Plain browser env
1393 mod(CodeMirror);
1394 })(function(CodeMirror) {
1395 "use strict";
1396
1397 var Pos = CodeMirror.Pos;
1398 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1399
1400 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";
1401 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1402 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1403
1404 function Iter(cm, line, ch, range) {
1405 this.line = line; this.ch = ch;
1406 this.cm = cm; this.text = cm.getLine(line);
1407 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1408 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1409 }
1410
1411 function tagAt(iter, ch) {
1412 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1413 return type && /\btag\b/.test(type);
1414 }
1415
1416 function nextLine(iter) {
1417 if (iter.line >= iter.max) return;
1418 iter.ch = 0;
1419 iter.text = iter.cm.getLine(++iter.line);
1420 return true;
1421 }
1422 function prevLine(iter) {
1423 if (iter.line <= iter.min) return;
1424 iter.text = iter.cm.getLine(--iter.line);
1425 iter.ch = iter.text.length;
1426 return true;
1427 }
1428
1429 function toTagEnd(iter) {
1430 for (;;) {
1431 var gt = iter.text.indexOf(">", iter.ch);
1432 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1433 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1434 var lastSlash = iter.text.lastIndexOf("/", gt);
1435 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1436 iter.ch = gt + 1;
1437 return selfClose ? "selfClose" : "regular";
1438 }
1439 }
1440 function toTagStart(iter) {
1441 for (;;) {
1442 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1443 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1444 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1445 xmlTagStart.lastIndex = lt;
1446 iter.ch = lt;
1447 var match = xmlTagStart.exec(iter.text);
1448 if (match && match.index == lt) return match;
1449 }
1450 }
1451
1452 function toNextTag(iter) {
1453 for (;;) {
1454 xmlTagStart.lastIndex = iter.ch;
1455 var found = xmlTagStart.exec(iter.text);
1456 if (!found) { if (nextLine(iter)) continue; else return; }
1457 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1458 iter.ch = found.index + found[0].length;
1459 return found;
1460 }
1461 }
1462 function toPrevTag(iter) {
1463 for (;;) {
1464 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1465 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1466 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1467 var lastSlash = iter.text.lastIndexOf("/", gt);
1468 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1469 iter.ch = gt + 1;
1470 return selfClose ? "selfClose" : "regular";
1471 }
1472 }
1473
1474 function findMatchingClose(iter, tag) {
1475 var stack = [];
1476 for (;;) {
1477 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1478 if (!next || !(end = toTagEnd(iter))) return;
1479 if (end == "selfClose") continue;
1480 if (next[1]) { // closing tag
1481 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1482 stack.length = i;
1483 break;
1484 }
1485 if (i < 0 && (!tag || tag == next[2])) return {
1486 tag: next[2],
1487 from: Pos(startLine, startCh),
1488 to: Pos(iter.line, iter.ch)
1489 };
1490 } else { // opening tag
1491 stack.push(next[2]);
1492 }
1493 }
1494 }
1495 function findMatchingOpen(iter, tag) {
1496 var stack = [];
1497 for (;;) {
1498 var prev = toPrevTag(iter);
1499 if (!prev) return;
1500 if (prev == "selfClose") { toTagStart(iter); continue; }
1501 var endLine = iter.line, endCh = iter.ch;
1502 var start = toTagStart(iter);
1503 if (!start) return;
1504 if (start[1]) { // closing tag
1505 stack.push(start[2]);
1506 } else { // opening tag
1507 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1508 stack.length = i;
1509 break;
1510 }
1511 if (i < 0 && (!tag || tag == start[2])) return {
1512 tag: start[2],
1513 from: Pos(iter.line, iter.ch),
1514 to: Pos(endLine, endCh)
1515 };
1516 }
1517 }
1518 }
1519
1520 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1521 var iter = new Iter(cm, start.line, 0);
1522 for (;;) {
1523 var openTag = toNextTag(iter), end;
1524 if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
1525 if (!openTag[1] && end != "selfClose") {
1526 var startPos = Pos(iter.line, iter.ch);
1527 var endPos = findMatchingClose(iter, openTag[2]);
1528 return endPos && {from: startPos, to: endPos.from};
1529 }
1530 }
1531 });
1532 CodeMirror.findMatchingTag = function(cm, pos, range) {
1533 var iter = new Iter(cm, pos.line, pos.ch, range);
1534 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1535 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1536 var start = end && toTagStart(iter);
1537 if (!end || !start || cmp(iter, pos) > 0) return;
1538 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1539 if (end == "selfClose") return {open: here, close: null, at: "open"};
1540
1541 if (start[1]) { // closing tag
1542 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1543 } else { // opening tag
1544 iter = new Iter(cm, to.line, to.ch, range);
1545 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1546 }
1547 };
1548
1549 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1550 var iter = new Iter(cm, pos.line, pos.ch, range);
1551 for (;;) {
1552 var open = findMatchingOpen(iter, tag);
1553 if (!open) break;
1554 var forward = new Iter(cm, pos.line, pos.ch, range);
1555 var close = findMatchingClose(forward, open.tag);
1556 if (close) return {open: open, close: close};
1557 }
1558 };
1559
1560 // Used by addon/edit/closetag.js
1561 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1562 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1563 return findMatchingClose(iter, name);
1564 };
1565 });
1566
1567 },{"../../lib/codemirror":14}],14:[function(require,module,exports){
1568 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1569 // Distributed under an MIT license: http://codemirror.net/LICENSE
1570
1571 // This is CodeMirror (http://codemirror.net), a code editor
1572 // implemented in JavaScript on top of the browser's DOM.
1573 //
1574 // You can find some technical background for some of the code below
1575 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1576
1577 (function (global, factory) {
1578 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1579 typeof define === 'function' && define.amd ? define(factory) :
1580 (global.CodeMirror = factory());
1581 }(this, (function () { 'use strict';
1582
1583 // Kludges for bugs and behavior differences that can't be feature
1584 // detected are enabled based on userAgent etc sniffing.
1585 var userAgent = navigator.userAgent;
1586 var platform = navigator.platform;
1587
1588 var gecko = /gecko\/\d/i.test(userAgent);
1589 var ie_upto10 = /MSIE \d/.test(userAgent);
1590 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1591 var edge = /Edge\/(\d+)/.exec(userAgent);
1592 var ie = ie_upto10 || ie_11up || edge;
1593 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1594 var webkit = !edge && /WebKit\//.test(userAgent);
1595 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1596 var chrome = !edge && /Chrome\//.test(userAgent);
1597 var presto = /Opera\//.test(userAgent);
1598 var safari = /Apple Computer/.test(navigator.vendor);
1599 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1600 var phantom = /PhantomJS/.test(userAgent);
1601
1602 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1603 var android = /Android/.test(userAgent);
1604 // This is woefully incomplete. Suggestions for alternative methods welcome.
1605 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1606 var mac = ios || /Mac/.test(platform);
1607 var chromeOS = /\bCrOS\b/.test(userAgent);
1608 var windows = /win/i.test(platform);
1609
1610 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1611 if (presto_version) { presto_version = Number(presto_version[1]); }
1612 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1613 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1614 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1615 var captureRightClick = gecko || (ie && ie_version >= 9);
1616
1617 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1618
1619 var rmClass = function(node, cls) {
1620 var current = node.className;
1621 var match = classTest(cls).exec(current);
1622 if (match) {
1623 var after = current.slice(match.index + match[0].length);
1624 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1625 }
1626 };
1627
1628 function removeChildren(e) {
1629 for (var count = e.childNodes.length; count > 0; --count)
1630 { e.removeChild(e.firstChild); }
1631 return e
1632 }
1633
1634 function removeChildrenAndAdd(parent, e) {
1635 return removeChildren(parent).appendChild(e)
1636 }
1637
1638 function elt(tag, content, className, style) {
1639 var e = document.createElement(tag);
1640 if (className) { e.className = className; }
1641 if (style) { e.style.cssText = style; }
1642 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1643 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1644 return e
1645 }
1646 // wrapper for elt, which removes the elt from the accessibility tree
1647 function eltP(tag, content, className, style) {
1648 var e = elt(tag, content, className, style);
1649 e.setAttribute("role", "presentation");
1650 return e
1651 }
1652
1653 var range;
1654 if (document.createRange) { range = function(node, start, end, endNode) {
1655 var r = document.createRange();
1656 r.setEnd(endNode || node, end);
1657 r.setStart(node, start);
1658 return r
1659 }; }
1660 else { range = function(node, start, end) {
1661 var r = document.body.createTextRange();
1662 try { r.moveToElementText(node.parentNode); }
1663 catch(e) { return r }
1664 r.collapse(true);
1665 r.moveEnd("character", end);
1666 r.moveStart("character", start);
1667 return r
1668 }; }
1669
1670 function contains(parent, child) {
1671 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1672 { child = child.parentNode; }
1673 if (parent.contains)
1674 { return parent.contains(child) }
1675 do {
1676 if (child.nodeType == 11) { child = child.host; }
1677 if (child == parent) { return true }
1678 } while (child = child.parentNode)
1679 }
1680
1681 function activeElt() {
1682 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1683 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1684 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1685 var activeElement;
1686 try {
1687 activeElement = document.activeElement;
1688 } catch(e) {
1689 activeElement = document.body || null;
1690 }
1691 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1692 { activeElement = activeElement.shadowRoot.activeElement; }
1693 return activeElement
1694 }
1695
1696 function addClass(node, cls) {
1697 var current = node.className;
1698 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1699 }
1700 function joinClasses(a, b) {
1701 var as = a.split(" ");
1702 for (var i = 0; i < as.length; i++)
1703 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1704 return b
1705 }
1706
1707 var selectInput = function(node) { node.select(); };
1708 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1709 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1710 else if (ie) // Suppress mysterious IE10 errors
1711 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1712
1713 function bind(f) {
1714 var args = Array.prototype.slice.call(arguments, 1);
1715 return function(){return f.apply(null, args)}
1716 }
1717
1718 function copyObj(obj, target, overwrite) {
1719 if (!target) { target = {}; }
1720 for (var prop in obj)
1721 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1722 { target[prop] = obj[prop]; } }
1723 return target
1724 }
1725
1726 // Counts the column offset in a string, taking tabs into account.
1727 // Used mostly to find indentation.
1728 function countColumn(string, end, tabSize, startIndex, startValue) {
1729 if (end == null) {
1730 end = string.search(/[^\s\u00a0]/);
1731 if (end == -1) { end = string.length; }
1732 }
1733 for (var i = startIndex || 0, n = startValue || 0;;) {
1734 var nextTab = string.indexOf("\t", i);
1735 if (nextTab < 0 || nextTab >= end)
1736 { return n + (end - i) }
1737 n += nextTab - i;
1738 n += tabSize - (n % tabSize);
1739 i = nextTab + 1;
1740 }
1741 }
1742
1743 var Delayed = function() {this.id = null;};
1744 Delayed.prototype.set = function (ms, f) {
1745 clearTimeout(this.id);
1746 this.id = setTimeout(f, ms);
1747 };
1748
1749 function indexOf(array, elt) {
1750 for (var i = 0; i < array.length; ++i)
1751 { if (array[i] == elt) { return i } }
1752 return -1
1753 }
1754
1755 // Number of pixels added to scroller and sizer to hide scrollbar
1756 var scrollerGap = 30;
1757
1758 // Returned or thrown by various protocols to signal 'I'm not
1759 // handling this'.
1760 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1761
1762 // Reused option objects for setSelection & friends
1763 var sel_dontScroll = {scroll: false};
1764 var sel_mouse = {origin: "*mouse"};
1765 var sel_move = {origin: "+move"};
1766
1767 // The inverse of countColumn -- find the offset that corresponds to
1768 // a particular column.
1769 function findColumn(string, goal, tabSize) {
1770 for (var pos = 0, col = 0;;) {
1771 var nextTab = string.indexOf("\t", pos);
1772 if (nextTab == -1) { nextTab = string.length; }
1773 var skipped = nextTab - pos;
1774 if (nextTab == string.length || col + skipped >= goal)
1775 { return pos + Math.min(skipped, goal - col) }
1776 col += nextTab - pos;
1777 col += tabSize - (col % tabSize);
1778 pos = nextTab + 1;
1779 if (col >= goal) { return pos }
1780 }
1781 }
1782
1783 var spaceStrs = [""];
1784 function spaceStr(n) {
1785 while (spaceStrs.length <= n)
1786 { spaceStrs.push(lst(spaceStrs) + " "); }
1787 return spaceStrs[n]
1788 }
1789
1790 function lst(arr) { return arr[arr.length-1] }
1791
1792 function map(array, f) {
1793 var out = [];
1794 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1795 return out
1796 }
1797
1798 function insertSorted(array, value, score) {
1799 var pos = 0, priority = score(value);
1800 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1801 array.splice(pos, 0, value);
1802 }
1803
1804 function nothing() {}
1805
1806 function createObj(base, props) {
1807 var inst;
1808 if (Object.create) {
1809 inst = Object.create(base);
1810 } else {
1811 nothing.prototype = base;
1812 inst = new nothing();
1813 }
1814 if (props) { copyObj(props, inst); }
1815 return inst
1816 }
1817
1818 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1819 function isWordCharBasic(ch) {
1820 return /\w/.test(ch) || ch > "\x80" &&
1821 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1822 }
1823 function isWordChar(ch, helper) {
1824 if (!helper) { return isWordCharBasic(ch) }
1825 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1826 return helper.test(ch)
1827 }
1828
1829 function isEmpty(obj) {
1830 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1831 return true
1832 }
1833
1834 // Extending unicode characters. A series of a non-extending char +
1835 // any number of extending chars is treated as a single unit as far
1836 // as editing and measuring is concerned. This is not fully correct,
1837 // since some scripts/fonts/browsers also treat other configurations
1838 // of code points as a group.
1839 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]/;
1840 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1841
1842 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1843 function skipExtendingChars(str, pos, dir) {
1844 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1845 return pos
1846 }
1847
1848 // Returns the value from the range [`from`; `to`] that satisfies
1849 // `pred` and is closest to `from`. Assumes that at least `to`
1850 // satisfies `pred`. Supports `from` being greater than `to`.
1851 function findFirst(pred, from, to) {
1852 // At any point we are certain `to` satisfies `pred`, don't know
1853 // whether `from` does.
1854 var dir = from > to ? -1 : 1;
1855 for (;;) {
1856 if (from == to) { return from }
1857 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1858 if (mid == from) { return pred(mid) ? from : to }
1859 if (pred(mid)) { to = mid; }
1860 else { from = mid + dir; }
1861 }
1862 }
1863
1864 // The display handles the DOM integration, both for input reading
1865 // and content drawing. It holds references to DOM nodes and
1866 // display-related state.
1867
1868 function Display(place, doc, input) {
1869 var d = this;
1870 this.input = input;
1871
1872 // Covers bottom-right square when both scrollbars are present.
1873 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1874 d.scrollbarFiller.setAttribute("cm-not-content", "true");
1875 // Covers bottom of gutter when coverGutterNextToScrollbar is on
1876 // and h scrollbar is present.
1877 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
1878 d.gutterFiller.setAttribute("cm-not-content", "true");
1879 // Will contain the actual code, positioned to cover the viewport.
1880 d.lineDiv = eltP("div", null, "CodeMirror-code");
1881 // Elements are added to these to represent selection and cursors.
1882 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1883 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
1884 // A visibility: hidden element used to find the size of things.
1885 d.measure = elt("div", null, "CodeMirror-measure");
1886 // When lines outside of the viewport are measured, they are drawn in this.
1887 d.lineMeasure = elt("div", null, "CodeMirror-measure");
1888 // Wraps everything that needs to exist inside the vertically-padded coordinate system
1889 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
1890 null, "position: relative; outline: none");
1891 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
1892 // Moved around its parent to cover visible view.
1893 d.mover = elt("div", [lines], null, "position: relative");
1894 // Set to the height of the document, allowing scrolling.
1895 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1896 d.sizerWidth = null;
1897 // Behavior of elts with overflow: auto and padding is
1898 // inconsistent across browsers. This is used to ensure the
1899 // scrollable area is big enough.
1900 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
1901 // Will contain the gutters, if any.
1902 d.gutters = elt("div", null, "CodeMirror-gutters");
1903 d.lineGutter = null;
1904 // Actual scrollable element.
1905 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
1906 d.scroller.setAttribute("tabIndex", "-1");
1907 // The element in which the editor lives.
1908 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
1909
1910 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
1911 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1912 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
1913
1914 if (place) {
1915 if (place.appendChild) { place.appendChild(d.wrapper); }
1916 else { place(d.wrapper); }
1917 }
1918
1919 // Current rendered range (may be bigger than the view window).
1920 d.viewFrom = d.viewTo = doc.first;
1921 d.reportedViewFrom = d.reportedViewTo = doc.first;
1922 // Information about the rendered lines.
1923 d.view = [];
1924 d.renderedView = null;
1925 // Holds info about a single rendered line when it was rendered
1926 // for measurement, while not in view.
1927 d.externalMeasured = null;
1928 // Empty space (in pixels) above the view
1929 d.viewOffset = 0;
1930 d.lastWrapHeight = d.lastWrapWidth = 0;
1931 d.updateLineNumbers = null;
1932
1933 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
1934 d.scrollbarsClipped = false;
1935
1936 // Used to only resize the line number gutter when necessary (when
1937 // the amount of lines crosses a boundary that makes its width change)
1938 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1939 // Set to true when a non-horizontal-scrolling line widget is
1940 // added. As an optimization, line widget aligning is skipped when
1941 // this is false.
1942 d.alignWidgets = false;
1943
1944 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
1945
1946 // Tracks the maximum line length so that the horizontal scrollbar
1947 // can be kept static when scrolling.
1948 d.maxLine = null;
1949 d.maxLineLength = 0;
1950 d.maxLineChanged = false;
1951
1952 // Used for measuring wheel scrolling granularity
1953 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
1954
1955 // True when shift is held down.
1956 d.shift = false;
1957
1958 // Used to track whether anything happened since the context menu
1959 // was opened.
1960 d.selForContextMenu = null;
1961
1962 d.activeTouch = null;
1963
1964 input.init(d);
1965 }
1966
1967 // Find the line object corresponding to the given line number.
1968 function getLine(doc, n) {
1969 n -= doc.first;
1970 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
1971 var chunk = doc;
1972 while (!chunk.lines) {
1973 for (var i = 0;; ++i) {
1974 var child = chunk.children[i], sz = child.chunkSize();
1975 if (n < sz) { chunk = child; break }
1976 n -= sz;
1977 }
1978 }
1979 return chunk.lines[n]
1980 }
1981
1982 // Get the part of a document between two positions, as an array of
1983 // strings.
1984 function getBetween(doc, start, end) {
1985 var out = [], n = start.line;
1986 doc.iter(start.line, end.line + 1, function (line) {
1987 var text = line.text;
1988 if (n == end.line) { text = text.slice(0, end.ch); }
1989 if (n == start.line) { text = text.slice(start.ch); }
1990 out.push(text);
1991 ++n;
1992 });
1993 return out
1994 }
1995 // Get the lines between from and to, as array of strings.
1996 function getLines(doc, from, to) {
1997 var out = [];
1998 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
1999 return out
2000 }
2001
2002 // Update the height of a line, propagating the height change
2003 // upwards to parent nodes.
2004 function updateLineHeight(line, height) {
2005 var diff = height - line.height;
2006 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
2007 }
2008
2009 // Given a line object, find its line number by walking up through
2010 // its parent links.
2011 function lineNo(line) {
2012 if (line.parent == null) { return null }
2013 var cur = line.parent, no = indexOf(cur.lines, line);
2014 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2015 for (var i = 0;; ++i) {
2016 if (chunk.children[i] == cur) { break }
2017 no += chunk.children[i].chunkSize();
2018 }
2019 }
2020 return no + cur.first
2021 }
2022
2023 // Find the line at the given vertical position, using the height
2024 // information in the document tree.
2025 function lineAtHeight(chunk, h) {
2026 var n = chunk.first;
2027 outer: do {
2028 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
2029 var child = chunk.children[i$1], ch = child.height;
2030 if (h < ch) { chunk = child; continue outer }
2031 h -= ch;
2032 n += child.chunkSize();
2033 }
2034 return n
2035 } while (!chunk.lines)
2036 var i = 0;
2037 for (; i < chunk.lines.length; ++i) {
2038 var line = chunk.lines[i], lh = line.height;
2039 if (h < lh) { break }
2040 h -= lh;
2041 }
2042 return n + i
2043 }
2044
2045 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
2046
2047 function lineNumberFor(options, i) {
2048 return String(options.lineNumberFormatter(i + options.firstLineNumber))
2049 }
2050
2051 // A Pos instance represents a position within the text.
2052 function Pos(line, ch, sticky) {
2053 if ( sticky === void 0 ) sticky = null;
2054
2055 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
2056 this.line = line;
2057 this.ch = ch;
2058 this.sticky = sticky;
2059 }
2060
2061 // Compare two positions, return 0 if they are the same, a negative
2062 // number when a is less, and a positive number otherwise.
2063 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
2064
2065 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
2066
2067 function copyPos(x) {return Pos(x.line, x.ch)}
2068 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
2069 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
2070
2071 // Most of the external API clips given positions to make sure they
2072 // actually exist within the document.
2073 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
2074 function clipPos(doc, pos) {
2075 if (pos.line < doc.first) { return Pos(doc.first, 0) }
2076 var last = doc.first + doc.size - 1;
2077 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
2078 return clipToLen(pos, getLine(doc, pos.line).text.length)
2079 }
2080 function clipToLen(pos, linelen) {
2081 var ch = pos.ch;
2082 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
2083 else if (ch < 0) { return Pos(pos.line, 0) }
2084 else { return pos }
2085 }
2086 function clipPosArray(doc, array) {
2087 var out = [];
2088 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
2089 return out
2090 }
2091
2092 // Optimize some code when these features are not used.
2093 var sawReadOnlySpans = false;
2094 var sawCollapsedSpans = false;
2095
2096 function seeReadOnlySpans() {
2097 sawReadOnlySpans = true;
2098 }
2099
2100 function seeCollapsedSpans() {
2101 sawCollapsedSpans = true;
2102 }
2103
2104 // TEXTMARKER SPANS
2105
2106 function MarkedSpan(marker, from, to) {
2107 this.marker = marker;
2108 this.from = from; this.to = to;
2109 }
2110
2111 // Search an array of spans for a span matching the given marker.
2112 function getMarkedSpanFor(spans, marker) {
2113 if (spans) { for (var i = 0; i < spans.length; ++i) {
2114 var span = spans[i];
2115 if (span.marker == marker) { return span }
2116 } }
2117 }
2118 // Remove a span from an array, returning undefined if no spans are
2119 // left (we don't store arrays for lines without spans).
2120 function removeMarkedSpan(spans, span) {
2121 var r;
2122 for (var i = 0; i < spans.length; ++i)
2123 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2124 return r
2125 }
2126 // Add a span to a line.
2127 function addMarkedSpan(line, span) {
2128 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2129 span.marker.attachLine(line);
2130 }
2131
2132 // Used for the algorithm that adjusts markers for a change in the
2133 // document. These functions cut an array of spans at a given
2134 // character position, returning an array of remaining chunks (or
2135 // undefined if nothing remains).
2136 function markedSpansBefore(old, startCh, isInsert) {
2137 var nw;
2138 if (old) { for (var i = 0; i < old.length; ++i) {
2139 var span = old[i], marker = span.marker;
2140 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2141 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2142 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));
2143 }
2144 } }
2145 return nw
2146 }
2147 function markedSpansAfter(old, endCh, isInsert) {
2148 var nw;
2149 if (old) { for (var i = 0; i < old.length; ++i) {
2150 var span = old[i], marker = span.marker;
2151 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2152 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2153 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2154 span.to == null ? null : span.to - endCh));
2155 }
2156 } }
2157 return nw
2158 }
2159
2160 // Given a change object, compute the new set of marker spans that
2161 // cover the line in which the change took place. Removes spans
2162 // entirely within the change, reconnects spans belonging to the
2163 // same marker that appear on both sides of the change, and cuts off
2164 // spans partially within the change. Returns an array of span
2165 // arrays with one element for each line in (after) the change.
2166 function stretchSpansOverChange(doc, change) {
2167 if (change.full) { return null }
2168 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2169 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2170 if (!oldFirst && !oldLast) { return null }
2171
2172 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2173 // Get the spans that 'stick out' on both sides
2174 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2175 var last = markedSpansAfter(oldLast, endCh, isInsert);
2176
2177 // Next, merge those two ends
2178 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2179 if (first) {
2180 // Fix up .to properties of first
2181 for (var i = 0; i < first.length; ++i) {
2182 var span = first[i];
2183 if (span.to == null) {
2184 var found = getMarkedSpanFor(last, span.marker);
2185 if (!found) { span.to = startCh; }
2186 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2187 }
2188 }
2189 }
2190 if (last) {
2191 // Fix up .from in last (or move them into first in case of sameLine)
2192 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2193 var span$1 = last[i$1];
2194 if (span$1.to != null) { span$1.to += offset; }
2195 if (span$1.from == null) {
2196 var found$1 = getMarkedSpanFor(first, span$1.marker);
2197 if (!found$1) {
2198 span$1.from = offset;
2199 if (sameLine) { (first || (first = [])).push(span$1); }
2200 }
2201 } else {
2202 span$1.from += offset;
2203 if (sameLine) { (first || (first = [])).push(span$1); }
2204 }
2205 }
2206 }
2207 // Make sure we didn't create any zero-length spans
2208 if (first) { first = clearEmptySpans(first); }
2209 if (last && last != first) { last = clearEmptySpans(last); }
2210
2211 var newMarkers = [first];
2212 if (!sameLine) {
2213 // Fill gap with whole-line-spans
2214 var gap = change.text.length - 2, gapMarkers;
2215 if (gap > 0 && first)
2216 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2217 { if (first[i$2].to == null)
2218 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2219 for (var i$3 = 0; i$3 < gap; ++i$3)
2220 { newMarkers.push(gapMarkers); }
2221 newMarkers.push(last);
2222 }
2223 return newMarkers
2224 }
2225
2226 // Remove spans that are empty and don't have a clearWhenEmpty
2227 // option of false.
2228 function clearEmptySpans(spans) {
2229 for (var i = 0; i < spans.length; ++i) {
2230 var span = spans[i];
2231 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
2232 { spans.splice(i--, 1); }
2233 }
2234 if (!spans.length) { return null }
2235 return spans
2236 }
2237
2238 // Used to 'clip' out readOnly ranges when making a change.
2239 function removeReadOnlyRanges(doc, from, to) {
2240 var markers = null;
2241 doc.iter(from.line, to.line + 1, function (line) {
2242 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
2243 var mark = line.markedSpans[i].marker;
2244 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
2245 { (markers || (markers = [])).push(mark); }
2246 } }
2247 });
2248 if (!markers) { return null }
2249 var parts = [{from: from, to: to}];
2250 for (var i = 0; i < markers.length; ++i) {
2251 var mk = markers[i], m = mk.find(0);
2252 for (var j = 0; j < parts.length; ++j) {
2253 var p = parts[j];
2254 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
2255 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
2256 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
2257 { newParts.push({from: p.from, to: m.from}); }
2258 if (dto > 0 || !mk.inclusiveRight && !dto)
2259 { newParts.push({from: m.to, to: p.to}); }
2260 parts.splice.apply(parts, newParts);
2261 j += newParts.length - 3;
2262 }
2263 }
2264 return parts
2265 }
2266
2267 // Connect or disconnect spans from a line.
2268 function detachMarkedSpans(line) {
2269 var spans = line.markedSpans;
2270 if (!spans) { return }
2271 for (var i = 0; i < spans.length; ++i)
2272 { spans[i].marker.detachLine(line); }
2273 line.markedSpans = null;
2274 }
2275 function attachMarkedSpans(line, spans) {
2276 if (!spans) { return }
2277 for (var i = 0; i < spans.length; ++i)
2278 { spans[i].marker.attachLine(line); }
2279 line.markedSpans = spans;
2280 }
2281
2282 // Helpers used when computing which overlapping collapsed span
2283 // counts as the larger one.
2284 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
2285 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
2286
2287 // Returns a number indicating which of two overlapping collapsed
2288 // spans is larger (and thus includes the other). Falls back to
2289 // comparing ids when the spans cover exactly the same range.
2290 function compareCollapsedMarkers(a, b) {
2291 var lenDiff = a.lines.length - b.lines.length;
2292 if (lenDiff != 0) { return lenDiff }
2293 var aPos = a.find(), bPos = b.find();
2294 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
2295 if (fromCmp) { return -fromCmp }
2296 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
2297 if (toCmp) { return toCmp }
2298 return b.id - a.id
2299 }
2300
2301 // Find out whether a line ends or starts in a collapsed span. If
2302 // so, return the marker for that span.
2303 function collapsedSpanAtSide(line, start) {
2304 var sps = sawCollapsedSpans && line.markedSpans, found;
2305 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2306 sp = sps[i];
2307 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
2308 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
2309 { found = sp.marker; }
2310 } }
2311 return found
2312 }
2313 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
2314 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
2315
2316 // Test whether there exists a collapsed span that partially
2317 // overlaps (covers the start or end, but not both) of a new span.
2318 // Such overlap is not allowed.
2319 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
2320 var line = getLine(doc, lineNo$$1);
2321 var sps = sawCollapsedSpans && line.markedSpans;
2322 if (sps) { for (var i = 0; i < sps.length; ++i) {
2323 var sp = sps[i];
2324 if (!sp.marker.collapsed) { continue }
2325 var found = sp.marker.find(0);
2326 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
2327 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
2328 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
2329 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
2330 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
2331 { return true }
2332 } }
2333 }
2334
2335 // A visual line is a line as drawn on the screen. Folding, for
2336 // example, can cause multiple logical lines to appear on the same
2337 // visual line. This finds the start of the visual line that the
2338 // given line is part of (usually that is the line itself).
2339 function visualLine(line) {
2340 var merged;
2341 while (merged = collapsedSpanAtStart(line))
2342 { line = merged.find(-1, true).line; }
2343 return line
2344 }
2345
2346 function visualLineEnd(line) {
2347 var merged;
2348 while (merged = collapsedSpanAtEnd(line))
2349 { line = merged.find(1, true).line; }
2350 return line
2351 }
2352
2353 // Returns an array of logical lines that continue the visual line
2354 // started by the argument, or undefined if there are no such lines.
2355 function visualLineContinued(line) {
2356 var merged, lines;
2357 while (merged = collapsedSpanAtEnd(line)) {
2358 line = merged.find(1, true).line
2359 ;(lines || (lines = [])).push(line);
2360 }
2361 return lines
2362 }
2363
2364 // Get the line number of the start of the visual line that the
2365 // given line number is part of.
2366 function visualLineNo(doc, lineN) {
2367 var line = getLine(doc, lineN), vis = visualLine(line);
2368 if (line == vis) { return lineN }
2369 return lineNo(vis)
2370 }
2371
2372 // Get the line number of the start of the next visual line after
2373 // the given line.
2374 function visualLineEndNo(doc, lineN) {
2375 if (lineN > doc.lastLine()) { return lineN }
2376 var line = getLine(doc, lineN), merged;
2377 if (!lineIsHidden(doc, line)) { return lineN }
2378 while (merged = collapsedSpanAtEnd(line))
2379 { line = merged.find(1, true).line; }
2380 return lineNo(line) + 1
2381 }
2382
2383 // Compute whether a line is hidden. Lines count as hidden when they
2384 // are part of a visual line that starts with another line, or when
2385 // they are entirely covered by collapsed, non-widget span.
2386 function lineIsHidden(doc, line) {
2387 var sps = sawCollapsedSpans && line.markedSpans;
2388 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2389 sp = sps[i];
2390 if (!sp.marker.collapsed) { continue }
2391 if (sp.from == null) { return true }
2392 if (sp.marker.widgetNode) { continue }
2393 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
2394 { return true }
2395 } }
2396 }
2397 function lineIsHiddenInner(doc, line, span) {
2398 if (span.to == null) {
2399 var end = span.marker.find(1, true);
2400 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
2401 }
2402 if (span.marker.inclusiveRight && span.to == line.text.length)
2403 { return true }
2404 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
2405 sp = line.markedSpans[i];
2406 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
2407 (sp.to == null || sp.to != span.from) &&
2408 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
2409 lineIsHiddenInner(doc, line, sp)) { return true }
2410 }
2411 }
2412
2413 // Find the height above the given line.
2414 function heightAtLine(lineObj) {
2415 lineObj = visualLine(lineObj);
2416
2417 var h = 0, chunk = lineObj.parent;
2418 for (var i = 0; i < chunk.lines.length; ++i) {
2419 var line = chunk.lines[i];
2420 if (line == lineObj) { break }
2421 else { h += line.height; }
2422 }
2423 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
2424 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
2425 var cur = p.children[i$1];
2426 if (cur == chunk) { break }
2427 else { h += cur.height; }
2428 }
2429 }
2430 return h
2431 }
2432
2433 // Compute the character length of a line, taking into account
2434 // collapsed ranges (see markText) that might hide parts, and join
2435 // other lines onto it.
2436 function lineLength(line) {
2437 if (line.height == 0) { return 0 }
2438 var len = line.text.length, merged, cur = line;
2439 while (merged = collapsedSpanAtStart(cur)) {
2440 var found = merged.find(0, true);
2441 cur = found.from.line;
2442 len += found.from.ch - found.to.ch;
2443 }
2444 cur = line;
2445 while (merged = collapsedSpanAtEnd(cur)) {
2446 var found$1 = merged.find(0, true);
2447 len -= cur.text.length - found$1.from.ch;
2448 cur = found$1.to.line;
2449 len += cur.text.length - found$1.to.ch;
2450 }
2451 return len
2452 }
2453
2454 // Find the longest line in the document.
2455 function findMaxLine(cm) {
2456 var d = cm.display, doc = cm.doc;
2457 d.maxLine = getLine(doc, doc.first);
2458 d.maxLineLength = lineLength(d.maxLine);
2459 d.maxLineChanged = true;
2460 doc.iter(function (line) {
2461 var len = lineLength(line);
2462 if (len > d.maxLineLength) {
2463 d.maxLineLength = len;
2464 d.maxLine = line;
2465 }
2466 });
2467 }
2468
2469 // BIDI HELPERS
2470
2471 function iterateBidiSections(order, from, to, f) {
2472 if (!order) { return f(from, to, "ltr", 0) }
2473 var found = false;
2474 for (var i = 0; i < order.length; ++i) {
2475 var part = order[i];
2476 if (part.from < to && part.to > from || from == to && part.to == from) {
2477 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
2478 found = true;
2479 }
2480 }
2481 if (!found) { f(from, to, "ltr"); }
2482 }
2483
2484 var bidiOther = null;
2485 function getBidiPartAt(order, ch, sticky) {
2486 var found;
2487 bidiOther = null;
2488 for (var i = 0; i < order.length; ++i) {
2489 var cur = order[i];
2490 if (cur.from < ch && cur.to > ch) { return i }
2491 if (cur.to == ch) {
2492 if (cur.from != cur.to && sticky == "before") { found = i; }
2493 else { bidiOther = i; }
2494 }
2495 if (cur.from == ch) {
2496 if (cur.from != cur.to && sticky != "before") { found = i; }
2497 else { bidiOther = i; }
2498 }
2499 }
2500 return found != null ? found : bidiOther
2501 }
2502
2503 // Bidirectional ordering algorithm
2504 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
2505 // that this (partially) implements.
2506
2507 // One-char codes used for character types:
2508 // L (L): Left-to-Right
2509 // R (R): Right-to-Left
2510 // r (AL): Right-to-Left Arabic
2511 // 1 (EN): European Number
2512 // + (ES): European Number Separator
2513 // % (ET): European Number Terminator
2514 // n (AN): Arabic Number
2515 // , (CS): Common Number Separator
2516 // m (NSM): Non-Spacing Mark
2517 // b (BN): Boundary Neutral
2518 // s (B): Paragraph Separator
2519 // t (S): Segment Separator
2520 // w (WS): Whitespace
2521 // N (ON): Other Neutrals
2522
2523 // Returns null if characters are ordered as they appear
2524 // (left-to-right), or an array of sections ({from, to, level}
2525 // objects) in the order in which they occur visually.
2526 var bidiOrdering = (function() {
2527 // Character types for codepoints 0 to 0xff
2528 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
2529 // Character types for codepoints 0x600 to 0x6f9
2530 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
2531 function charType(code) {
2532 if (code <= 0xf7) { return lowTypes.charAt(code) }
2533 else if (0x590 <= code && code <= 0x5f4) { return "R" }
2534 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
2535 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
2536 else if (0x2000 <= code && code <= 0x200b) { return "w" }
2537 else if (code == 0x200c) { return "b" }
2538 else { return "L" }
2539 }
2540
2541 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2542 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
2543
2544 function BidiSpan(level, from, to) {
2545 this.level = level;
2546 this.from = from; this.to = to;
2547 }
2548
2549 return function(str, direction) {
2550 var outerType = direction == "ltr" ? "L" : "R";
2551
2552 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
2553 var len = str.length, types = [];
2554 for (var i = 0; i < len; ++i)
2555 { types.push(charType(str.charCodeAt(i))); }
2556
2557 // W1. Examine each non-spacing mark (NSM) in the level run, and
2558 // change the type of the NSM to the type of the previous
2559 // character. If the NSM is at the start of the level run, it will
2560 // get the type of sor.
2561 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2562 var type = types[i$1];
2563 if (type == "m") { types[i$1] = prev; }
2564 else { prev = type; }
2565 }
2566
2567 // W2. Search backwards from each instance of a European number
2568 // until the first strong type (R, L, AL, or sor) is found. If an
2569 // AL is found, change the type of the European number to Arabic
2570 // number.
2571 // W3. Change all ALs to R.
2572 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2573 var type$1 = types[i$2];
2574 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2575 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2576 }
2577
2578 // W4. A single European separator between two European numbers
2579 // changes to a European number. A single common separator between
2580 // two numbers of the same type changes to that type.
2581 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2582 var type$2 = types[i$3];
2583 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2584 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2585 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2586 prev$1 = type$2;
2587 }
2588
2589 // W5. A sequence of European terminators adjacent to European
2590 // numbers changes to all European numbers.
2591 // W6. Otherwise, separators and terminators change to Other
2592 // Neutral.
2593 for (var i$4 = 0; i$4 < len; ++i$4) {
2594 var type$3 = types[i$4];
2595 if (type$3 == ",") { types[i$4] = "N"; }
2596 else if (type$3 == "%") {
2597 var end = (void 0);
2598 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2599 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2600 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2601 i$4 = end - 1;
2602 }
2603 }
2604
2605 // W7. Search backwards from each instance of a European number
2606 // until the first strong type (R, L, or sor) is found. If an L is
2607 // found, then change the type of the European number to L.
2608 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2609 var type$4 = types[i$5];
2610 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2611 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2612 }
2613
2614 // N1. A sequence of neutrals takes the direction of the
2615 // surrounding strong text if the text on both sides has the same
2616 // direction. European and Arabic numbers act as if they were R in
2617 // terms of their influence on neutrals. Start-of-level-run (sor)
2618 // and end-of-level-run (eor) are used at level run boundaries.
2619 // N2. Any remaining neutrals take the embedding direction.
2620 for (var i$6 = 0; i$6 < len; ++i$6) {
2621 if (isNeutral.test(types[i$6])) {
2622 var end$1 = (void 0);
2623 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2624 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2625 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2626 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2627 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2628 i$6 = end$1 - 1;
2629 }
2630 }
2631
2632 // Here we depart from the documented algorithm, in order to avoid
2633 // building up an actual levels array. Since there are only three
2634 // levels (0, 1, 2) in an implementation that doesn't take
2635 // explicit embedding into account, we can build up the order on
2636 // the fly, without following the level-based algorithm.
2637 var order = [], m;
2638 for (var i$7 = 0; i$7 < len;) {
2639 if (countsAsLeft.test(types[i$7])) {
2640 var start = i$7;
2641 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2642 order.push(new BidiSpan(0, start, i$7));
2643 } else {
2644 var pos = i$7, at = order.length;
2645 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2646 for (var j$2 = pos; j$2 < i$7;) {
2647 if (countsAsNum.test(types[j$2])) {
2648 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2649 var nstart = j$2;
2650 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2651 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2652 pos = j$2;
2653 } else { ++j$2; }
2654 }
2655 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2656 }
2657 }
2658 if (direction == "ltr") {
2659 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2660 order[0].from = m[0].length;
2661 order.unshift(new BidiSpan(0, 0, m[0].length));
2662 }
2663 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2664 lst(order).to -= m[0].length;
2665 order.push(new BidiSpan(0, len - m[0].length, len));
2666 }
2667 }
2668
2669 return direction == "rtl" ? order.reverse() : order
2670 }
2671 })();
2672
2673 // Get the bidi ordering for the given line (and cache it). Returns
2674 // false for lines that are fully left-to-right, and an array of
2675 // BidiSpan objects otherwise.
2676 function getOrder(line, direction) {
2677 var order = line.order;
2678 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2679 return order
2680 }
2681
2682 // EVENT HANDLING
2683
2684 // Lightweight event framework. on/off also work on DOM nodes,
2685 // registering native DOM handlers.
2686
2687 var noHandlers = [];
2688
2689 var on = function(emitter, type, f) {
2690 if (emitter.addEventListener) {
2691 emitter.addEventListener(type, f, false);
2692 } else if (emitter.attachEvent) {
2693 emitter.attachEvent("on" + type, f);
2694 } else {
2695 var map$$1 = emitter._handlers || (emitter._handlers = {});
2696 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2697 }
2698 };
2699
2700 function getHandlers(emitter, type) {
2701 return emitter._handlers && emitter._handlers[type] || noHandlers
2702 }
2703
2704 function off(emitter, type, f) {
2705 if (emitter.removeEventListener) {
2706 emitter.removeEventListener(type, f, false);
2707 } else if (emitter.detachEvent) {
2708 emitter.detachEvent("on" + type, f);
2709 } else {
2710 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2711 if (arr) {
2712 var index = indexOf(arr, f);
2713 if (index > -1)
2714 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2715 }
2716 }
2717 }
2718
2719 function signal(emitter, type /*, values...*/) {
2720 var handlers = getHandlers(emitter, type);
2721 if (!handlers.length) { return }
2722 var args = Array.prototype.slice.call(arguments, 2);
2723 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2724 }
2725
2726 // The DOM events that CodeMirror handles can be overridden by
2727 // registering a (non-DOM) handler on the editor for the event name,
2728 // and preventDefault-ing the event in that handler.
2729 function signalDOMEvent(cm, e, override) {
2730 if (typeof e == "string")
2731 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2732 signal(cm, override || e.type, cm, e);
2733 return e_defaultPrevented(e) || e.codemirrorIgnore
2734 }
2735
2736 function signalCursorActivity(cm) {
2737 var arr = cm._handlers && cm._handlers.cursorActivity;
2738 if (!arr) { return }
2739 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2740 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2741 { set.push(arr[i]); } }
2742 }
2743
2744 function hasHandler(emitter, type) {
2745 return getHandlers(emitter, type).length > 0
2746 }
2747
2748 // Add on and off methods to a constructor's prototype, to make
2749 // registering events on such objects more convenient.
2750 function eventMixin(ctor) {
2751 ctor.prototype.on = function(type, f) {on(this, type, f);};
2752 ctor.prototype.off = function(type, f) {off(this, type, f);};
2753 }
2754
2755 // Due to the fact that we still support jurassic IE versions, some
2756 // compatibility wrappers are needed.
2757
2758 function e_preventDefault(e) {
2759 if (e.preventDefault) { e.preventDefault(); }
2760 else { e.returnValue = false; }
2761 }
2762 function e_stopPropagation(e) {
2763 if (e.stopPropagation) { e.stopPropagation(); }
2764 else { e.cancelBubble = true; }
2765 }
2766 function e_defaultPrevented(e) {
2767 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2768 }
2769 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2770
2771 function e_target(e) {return e.target || e.srcElement}
2772 function e_button(e) {
2773 var b = e.which;
2774 if (b == null) {
2775 if (e.button & 1) { b = 1; }
2776 else if (e.button & 2) { b = 3; }
2777 else if (e.button & 4) { b = 2; }
2778 }
2779 if (mac && e.ctrlKey && b == 1) { b = 3; }
2780 return b
2781 }
2782
2783 // Detect drag-and-drop
2784 var dragAndDrop = function() {
2785 // There is *some* kind of drag-and-drop support in IE6-8, but I
2786 // couldn't get it to work yet.
2787 if (ie && ie_version < 9) { return false }
2788 var div = elt('div');
2789 return "draggable" in div || "dragDrop" in div
2790 }();
2791
2792 var zwspSupported;
2793 function zeroWidthElement(measure) {
2794 if (zwspSupported == null) {
2795 var test = elt("span", "\u200b");
2796 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2797 if (measure.firstChild.offsetHeight != 0)
2798 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2799 }
2800 var node = zwspSupported ? elt("span", "\u200b") :
2801 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2802 node.setAttribute("cm-text", "");
2803 return node
2804 }
2805
2806 // Feature-detect IE's crummy client rect reporting for bidi text
2807 var badBidiRects;
2808 function hasBadBidiRects(measure) {
2809 if (badBidiRects != null) { return badBidiRects }
2810 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2811 var r0 = range(txt, 0, 1).getBoundingClientRect();
2812 var r1 = range(txt, 1, 2).getBoundingClientRect();
2813 removeChildren(measure);
2814 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2815 return badBidiRects = (r1.right - r0.right < 3)
2816 }
2817
2818 // See if "".split is the broken IE version, if so, provide an
2819 // alternative way to split lines.
2820 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2821 var pos = 0, result = [], l = string.length;
2822 while (pos <= l) {
2823 var nl = string.indexOf("\n", pos);
2824 if (nl == -1) { nl = string.length; }
2825 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2826 var rt = line.indexOf("\r");
2827 if (rt != -1) {
2828 result.push(line.slice(0, rt));
2829 pos += rt + 1;
2830 } else {
2831 result.push(line);
2832 pos = nl + 1;
2833 }
2834 }
2835 return result
2836 } : function (string) { return string.split(/\r\n?|\n/); };
2837
2838 var hasSelection = window.getSelection ? function (te) {
2839 try { return te.selectionStart != te.selectionEnd }
2840 catch(e) { return false }
2841 } : function (te) {
2842 var range$$1;
2843 try {range$$1 = te.ownerDocument.selection.createRange();}
2844 catch(e) {}
2845 if (!range$$1 || range$$1.parentElement() != te) { return false }
2846 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2847 };
2848
2849 var hasCopyEvent = (function () {
2850 var e = elt("div");
2851 if ("oncopy" in e) { return true }
2852 e.setAttribute("oncopy", "return;");
2853 return typeof e.oncopy == "function"
2854 })();
2855
2856 var badZoomedRects = null;
2857 function hasBadZoomedRects(measure) {
2858 if (badZoomedRects != null) { return badZoomedRects }
2859 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2860 var normal = node.getBoundingClientRect();
2861 var fromRange = range(node, 0, 1).getBoundingClientRect();
2862 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2863 }
2864
2865 // Known modes, by name and by MIME
2866 var modes = {};
2867 var mimeModes = {};
2868
2869 // Extra arguments are stored as the mode's dependencies, which is
2870 // used by (legacy) mechanisms like loadmode.js to automatically
2871 // load a mode. (Preferred mechanism is the require/define calls.)
2872 function defineMode(name, mode) {
2873 if (arguments.length > 2)
2874 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2875 modes[name] = mode;
2876 }
2877
2878 function defineMIME(mime, spec) {
2879 mimeModes[mime] = spec;
2880 }
2881
2882 // Given a MIME type, a {name, ...options} config object, or a name
2883 // string, return a mode config object.
2884 function resolveMode(spec) {
2885 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2886 spec = mimeModes[spec];
2887 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2888 var found = mimeModes[spec.name];
2889 if (typeof found == "string") { found = {name: found}; }
2890 spec = createObj(found, spec);
2891 spec.name = found.name;
2892 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2893 return resolveMode("application/xml")
2894 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2895 return resolveMode("application/json")
2896 }
2897 if (typeof spec == "string") { return {name: spec} }
2898 else { return spec || {name: "null"} }
2899 }
2900
2901 // Given a mode spec (anything that resolveMode accepts), find and
2902 // initialize an actual mode object.
2903 function getMode(options, spec) {
2904 spec = resolveMode(spec);
2905 var mfactory = modes[spec.name];
2906 if (!mfactory) { return getMode(options, "text/plain") }
2907 var modeObj = mfactory(options, spec);
2908 if (modeExtensions.hasOwnProperty(spec.name)) {
2909 var exts = modeExtensions[spec.name];
2910 for (var prop in exts) {
2911 if (!exts.hasOwnProperty(prop)) { continue }
2912 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2913 modeObj[prop] = exts[prop];
2914 }
2915 }
2916 modeObj.name = spec.name;
2917 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2918 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2919 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2920
2921 return modeObj
2922 }
2923
2924 // This can be used to attach properties to mode objects from
2925 // outside the actual mode definition.
2926 var modeExtensions = {};
2927 function extendMode(mode, properties) {
2928 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2929 copyObj(properties, exts);
2930 }
2931
2932 function copyState(mode, state) {
2933 if (state === true) { return state }
2934 if (mode.copyState) { return mode.copyState(state) }
2935 var nstate = {};
2936 for (var n in state) {
2937 var val = state[n];
2938 if (val instanceof Array) { val = val.concat([]); }
2939 nstate[n] = val;
2940 }
2941 return nstate
2942 }
2943
2944 // Given a mode and a state (for that mode), find the inner mode and
2945 // state at the position that the state refers to.
2946 function innerMode(mode, state) {
2947 var info;
2948 while (mode.innerMode) {
2949 info = mode.innerMode(state);
2950 if (!info || info.mode == mode) { break }
2951 state = info.state;
2952 mode = info.mode;
2953 }
2954 return info || {mode: mode, state: state}
2955 }
2956
2957 function startState(mode, a1, a2) {
2958 return mode.startState ? mode.startState(a1, a2) : true
2959 }
2960
2961 // STRING STREAM
2962
2963 // Fed to the mode parsers, provides helper functions to make
2964 // parsers more succinct.
2965
2966 var StringStream = function(string, tabSize, lineOracle) {
2967 this.pos = this.start = 0;
2968 this.string = string;
2969 this.tabSize = tabSize || 8;
2970 this.lastColumnPos = this.lastColumnValue = 0;
2971 this.lineStart = 0;
2972 this.lineOracle = lineOracle;
2973 };
2974
2975 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
2976 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
2977 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
2978 StringStream.prototype.next = function () {
2979 if (this.pos < this.string.length)
2980 { return this.string.charAt(this.pos++) }
2981 };
2982 StringStream.prototype.eat = function (match) {
2983 var ch = this.string.charAt(this.pos);
2984 var ok;
2985 if (typeof match == "string") { ok = ch == match; }
2986 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
2987 if (ok) {++this.pos; return ch}
2988 };
2989 StringStream.prototype.eatWhile = function (match) {
2990 var start = this.pos;
2991 while (this.eat(match)){}
2992 return this.pos > start
2993 };
2994 StringStream.prototype.eatSpace = function () {
2995 var this$1 = this;
2996
2997 var start = this.pos;
2998 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
2999 return this.pos > start
3000 };
3001 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
3002 StringStream.prototype.skipTo = function (ch) {
3003 var found = this.string.indexOf(ch, this.pos);
3004 if (found > -1) {this.pos = found; return true}
3005 };
3006 StringStream.prototype.backUp = function (n) {this.pos -= n;};
3007 StringStream.prototype.column = function () {
3008 if (this.lastColumnPos < this.start) {
3009 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3010 this.lastColumnPos = this.start;
3011 }
3012 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3013 };
3014 StringStream.prototype.indentation = function () {
3015 return countColumn(this.string, null, this.tabSize) -
3016 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3017 };
3018 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
3019 if (typeof pattern == "string") {
3020 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
3021 var substr = this.string.substr(this.pos, pattern.length);
3022 if (cased(substr) == cased(pattern)) {
3023 if (consume !== false) { this.pos += pattern.length; }
3024 return true
3025 }
3026 } else {
3027 var match = this.string.slice(this.pos).match(pattern);
3028 if (match && match.index > 0) { return null }
3029 if (match && consume !== false) { this.pos += match[0].length; }
3030 return match
3031 }
3032 };
3033 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
3034 StringStream.prototype.hideFirstChars = function (n, inner) {
3035 this.lineStart += n;
3036 try { return inner() }
3037 finally { this.lineStart -= n; }
3038 };
3039 StringStream.prototype.lookAhead = function (n) {
3040 var oracle = this.lineOracle;
3041 return oracle && oracle.lookAhead(n)
3042 };
3043 StringStream.prototype.baseToken = function () {
3044 var oracle = this.lineOracle;
3045 return oracle && oracle.baseToken(this.pos)
3046 };
3047
3048 var SavedContext = function(state, lookAhead) {
3049 this.state = state;
3050 this.lookAhead = lookAhead;
3051 };
3052
3053 var Context = function(doc, state, line, lookAhead) {
3054 this.state = state;
3055 this.doc = doc;
3056 this.line = line;
3057 this.maxLookAhead = lookAhead || 0;
3058 this.baseTokens = null;
3059 this.baseTokenPos = 1;
3060 };
3061
3062 Context.prototype.lookAhead = function (n) {
3063 var line = this.doc.getLine(this.line + n);
3064 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
3065 return line
3066 };
3067
3068 Context.prototype.baseToken = function (n) {
3069 var this$1 = this;
3070
3071 if (!this.baseTokens) { return null }
3072 while (this.baseTokens[this.baseTokenPos] <= n)
3073 { this$1.baseTokenPos += 2; }
3074 var type = this.baseTokens[this.baseTokenPos + 1];
3075 return {type: type && type.replace(/( |^)overlay .*/, ""),
3076 size: this.baseTokens[this.baseTokenPos] - n}
3077 };
3078
3079 Context.prototype.nextLine = function () {
3080 this.line++;
3081 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
3082 };
3083
3084 Context.fromSaved = function (doc, saved, line) {
3085 if (saved instanceof SavedContext)
3086 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
3087 else
3088 { return new Context(doc, copyState(doc.mode, saved), line) }
3089 };
3090
3091 Context.prototype.save = function (copy) {
3092 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
3093 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
3094 };
3095
3096
3097 // Compute a style array (an array starting with a mode generation
3098 // -- for invalidation -- followed by pairs of end positions and
3099 // style strings), which is used to highlight the tokens on the
3100 // line.
3101 function highlightLine(cm, line, context, forceToEnd) {
3102 // A styles array always starts with a number identifying the
3103 // mode/overlays that it is based on (for easy invalidation).
3104 var st = [cm.state.modeGen], lineClasses = {};
3105 // Compute the base array of styles
3106 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
3107 lineClasses, forceToEnd);
3108 var state = context.state;
3109
3110 // Run overlays, adjust style array.
3111 var loop = function ( o ) {
3112 context.baseTokens = st;
3113 var overlay = cm.state.overlays[o], i = 1, at = 0;
3114 context.state = true;
3115 runMode(cm, line.text, overlay.mode, context, function (end, style) {
3116 var start = i;
3117 // Ensure there's a token end at the current position, and that i points at it
3118 while (at < end) {
3119 var i_end = st[i];
3120 if (i_end > end)
3121 { st.splice(i, 1, end, st[i+1], i_end); }
3122 i += 2;
3123 at = Math.min(end, i_end);
3124 }
3125 if (!style) { return }
3126 if (overlay.opaque) {
3127 st.splice(start, i - start, end, "overlay " + style);
3128 i = start + 2;
3129 } else {
3130 for (; start < i; start += 2) {
3131 var cur = st[start+1];
3132 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
3133 }
3134 }
3135 }, lineClasses);
3136 context.state = state;
3137 context.baseTokens = null;
3138 context.baseTokenPos = 1;
3139 };
3140
3141 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
3142
3143 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
3144 }
3145
3146 function getLineStyles(cm, line, updateFrontier) {
3147 if (!line.styles || line.styles[0] != cm.state.modeGen) {
3148 var context = getContextBefore(cm, lineNo(line));
3149 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
3150 var result = highlightLine(cm, line, context);
3151 if (resetState) { context.state = resetState; }
3152 line.stateAfter = context.save(!resetState);
3153 line.styles = result.styles;
3154 if (result.classes) { line.styleClasses = result.classes; }
3155 else if (line.styleClasses) { line.styleClasses = null; }
3156 if (updateFrontier === cm.doc.highlightFrontier)
3157 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
3158 }
3159 return line.styles
3160 }
3161
3162 function getContextBefore(cm, n, precise) {
3163 var doc = cm.doc, display = cm.display;
3164 if (!doc.mode.startState) { return new Context(doc, true, n) }
3165 var start = findStartLine(cm, n, precise);
3166 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
3167 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
3168
3169 doc.iter(start, n, function (line) {
3170 processLine(cm, line.text, context);
3171 var pos = context.line;
3172 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
3173 context.nextLine();
3174 });
3175 if (precise) { doc.modeFrontier = context.line; }
3176 return context
3177 }
3178
3179 // Lightweight form of highlight -- proceed over this line and
3180 // update state, but don't save a style array. Used for lines that
3181 // aren't currently visible.
3182 function processLine(cm, text, context, startAt) {
3183 var mode = cm.doc.mode;
3184 var stream = new StringStream(text, cm.options.tabSize, context);
3185 stream.start = stream.pos = startAt || 0;
3186 if (text == "") { callBlankLine(mode, context.state); }
3187 while (!stream.eol()) {
3188 readToken(mode, stream, context.state);
3189 stream.start = stream.pos;
3190 }
3191 }
3192
3193 function callBlankLine(mode, state) {
3194 if (mode.blankLine) { return mode.blankLine(state) }
3195 if (!mode.innerMode) { return }
3196 var inner = innerMode(mode, state);
3197 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
3198 }
3199
3200 function readToken(mode, stream, state, inner) {
3201 for (var i = 0; i < 10; i++) {
3202 if (inner) { inner[0] = innerMode(mode, state).mode; }
3203 var style = mode.token(stream, state);
3204 if (stream.pos > stream.start) { return style }
3205 }
3206 throw new Error("Mode " + mode.name + " failed to advance stream.")
3207 }
3208
3209 var Token = function(stream, type, state) {
3210 this.start = stream.start; this.end = stream.pos;
3211 this.string = stream.current();
3212 this.type = type || null;
3213 this.state = state;
3214 };
3215
3216 // Utility for getTokenAt and getLineTokens
3217 function takeToken(cm, pos, precise, asArray) {
3218 var doc = cm.doc, mode = doc.mode, style;
3219 pos = clipPos(doc, pos);
3220 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
3221 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
3222 if (asArray) { tokens = []; }
3223 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
3224 stream.start = stream.pos;
3225 style = readToken(mode, stream, context.state);
3226 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
3227 }
3228 return asArray ? tokens : new Token(stream, style, context.state)
3229 }
3230
3231 function extractLineClasses(type, output) {
3232 if (type) { for (;;) {
3233 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
3234 if (!lineClass) { break }
3235 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
3236 var prop = lineClass[1] ? "bgClass" : "textClass";
3237 if (output[prop] == null)
3238 { output[prop] = lineClass[2]; }
3239 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
3240 { output[prop] += " " + lineClass[2]; }
3241 } }
3242 return type
3243 }
3244
3245 // Run the given mode's parser over a line, calling f for each token.
3246 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
3247 var flattenSpans = mode.flattenSpans;
3248 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
3249 var curStart = 0, curStyle = null;
3250 var stream = new StringStream(text, cm.options.tabSize, context), style;
3251 var inner = cm.options.addModeClass && [null];
3252 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
3253 while (!stream.eol()) {
3254 if (stream.pos > cm.options.maxHighlightLength) {
3255 flattenSpans = false;
3256 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
3257 stream.pos = text.length;
3258 style = null;
3259 } else {
3260 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
3261 }
3262 if (inner) {
3263 var mName = inner[0].name;
3264 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
3265 }
3266 if (!flattenSpans || curStyle != style) {
3267 while (curStart < stream.start) {
3268 curStart = Math.min(stream.start, curStart + 5000);
3269 f(curStart, curStyle);
3270 }
3271 curStyle = style;
3272 }
3273 stream.start = stream.pos;
3274 }
3275 while (curStart < stream.pos) {
3276 // Webkit seems to refuse to render text nodes longer than 57444
3277 // characters, and returns inaccurate measurements in nodes
3278 // starting around 5000 chars.
3279 var pos = Math.min(stream.pos, curStart + 5000);
3280 f(pos, curStyle);
3281 curStart = pos;
3282 }
3283 }
3284
3285 // Finds the line to start with when starting a parse. Tries to
3286 // find a line with a stateAfter, so that it can start with a
3287 // valid state. If that fails, it returns the line with the
3288 // smallest indentation, which tends to need the least context to
3289 // parse correctly.
3290 function findStartLine(cm, n, precise) {
3291 var minindent, minline, doc = cm.doc;
3292 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
3293 for (var search = n; search > lim; --search) {
3294 if (search <= doc.first) { return doc.first }
3295 var line = getLine(doc, search - 1), after = line.stateAfter;
3296 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
3297 { return search }
3298 var indented = countColumn(line.text, null, cm.options.tabSize);
3299 if (minline == null || minindent > indented) {
3300 minline = search - 1;
3301 minindent = indented;
3302 }
3303 }
3304 return minline
3305 }
3306
3307 function retreatFrontier(doc, n) {
3308 doc.modeFrontier = Math.min(doc.modeFrontier, n);
3309 if (doc.highlightFrontier < n - 10) { return }
3310 var start = doc.first;
3311 for (var line = n - 1; line > start; line--) {
3312 var saved = getLine(doc, line).stateAfter;
3313 // change is on 3
3314 // state on line 1 looked ahead 2 -- so saw 3
3315 // test 1 + 2 < 3 should cover this
3316 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
3317 start = line + 1;
3318 break
3319 }
3320 }
3321 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
3322 }
3323
3324 // LINE DATA STRUCTURE
3325
3326 // Line objects. These hold state related to a line, including
3327 // highlighting info (the styles array).
3328 var Line = function(text, markedSpans, estimateHeight) {
3329 this.text = text;
3330 attachMarkedSpans(this, markedSpans);
3331 this.height = estimateHeight ? estimateHeight(this) : 1;
3332 };
3333
3334 Line.prototype.lineNo = function () { return lineNo(this) };
3335 eventMixin(Line);
3336
3337 // Change the content (text, markers) of a line. Automatically
3338 // invalidates cached information and tries to re-estimate the
3339 // line's height.
3340 function updateLine(line, text, markedSpans, estimateHeight) {
3341 line.text = text;
3342 if (line.stateAfter) { line.stateAfter = null; }
3343 if (line.styles) { line.styles = null; }
3344 if (line.order != null) { line.order = null; }
3345 detachMarkedSpans(line);
3346 attachMarkedSpans(line, markedSpans);
3347 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3348 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3349 }
3350
3351 // Detach a line from the document tree and its markers.
3352 function cleanUpLine(line) {
3353 line.parent = null;
3354 detachMarkedSpans(line);
3355 }
3356
3357 // Convert a style as returned by a mode (either null, or a string
3358 // containing one or more styles) to a CSS style. This is cached,
3359 // and also looks for line-wide styles.
3360 var styleToClassCache = {};
3361 var styleToClassCacheWithMode = {};
3362 function interpretTokenStyle(style, options) {
3363 if (!style || /^\s*$/.test(style)) { return null }
3364 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3365 return cache[style] ||
3366 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3367 }
3368
3369 // Render the DOM representation of the text of a line. Also builds
3370 // up a 'line map', which points at the DOM nodes that represent
3371 // specific stretches of text, and is used by the measuring code.
3372 // The returned object contains the DOM node, this map, and
3373 // information about line-wide styles that were set by the mode.
3374 function buildLineContent(cm, lineView) {
3375 // The padding-right forces the element to have a 'border', which
3376 // is needed on Webkit to be able to get line-level bounding
3377 // rectangles for it (in measureChar).
3378 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3379 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3380 col: 0, pos: 0, cm: cm,
3381 trailingSpace: false,
3382 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
3383 lineView.measure = {};
3384
3385 // Iterate over the logical lines that make up this visual line.
3386 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3387 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3388 builder.pos = 0;
3389 builder.addToken = buildToken;
3390 // Optionally wire in some hacks into the token-rendering
3391 // algorithm, to deal with browser quirks.
3392 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3393 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3394 builder.map = [];
3395 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3396 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3397 if (line.styleClasses) {
3398 if (line.styleClasses.bgClass)
3399 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3400 if (line.styleClasses.textClass)
3401 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3402 }
3403
3404 // Ensure at least a single node is present, for measuring.
3405 if (builder.map.length == 0)
3406 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3407
3408 // Store the map and a cache object for the current logical line
3409 if (i == 0) {
3410 lineView.measure.map = builder.map;
3411 lineView.measure.cache = {};
3412 } else {
3413 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3414 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3415 }
3416 }
3417
3418 // See issue #2901
3419 if (webkit) {
3420 var last = builder.content.lastChild;
3421 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3422 { builder.content.className = "cm-tab-wrap-hack"; }
3423 }
3424
3425 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3426 if (builder.pre.className)
3427 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3428
3429 return builder
3430 }
3431
3432 function defaultSpecialCharPlaceholder(ch) {
3433 var token = elt("span", "\u2022", "cm-invalidchar");
3434 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3435 token.setAttribute("aria-label", token.title);
3436 return token
3437 }
3438
3439 // Build up the DOM representation for a single token, and add it to
3440 // the line map. Takes care to render special characters separately.
3441 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
3442 if (!text) { return }
3443 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3444 var special = builder.cm.state.specialChars, mustWrap = false;
3445 var content;
3446 if (!special.test(text)) {
3447 builder.col += text.length;
3448 content = document.createTextNode(displayText);
3449 builder.map.push(builder.pos, builder.pos + text.length, content);
3450 if (ie && ie_version < 9) { mustWrap = true; }
3451 builder.pos += text.length;
3452 } else {
3453 content = document.createDocumentFragment();
3454 var pos = 0;
3455 while (true) {
3456 special.lastIndex = pos;
3457 var m = special.exec(text);
3458 var skipped = m ? m.index - pos : text.length - pos;
3459 if (skipped) {
3460 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3461 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3462 else { content.appendChild(txt); }
3463 builder.map.push(builder.pos, builder.pos + skipped, txt);
3464 builder.col += skipped;
3465 builder.pos += skipped;
3466 }
3467 if (!m) { break }
3468 pos += skipped + 1;
3469 var txt$1 = (void 0);
3470 if (m[0] == "\t") {
3471 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3472 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3473 txt$1.setAttribute("role", "presentation");
3474 txt$1.setAttribute("cm-text", "\t");
3475 builder.col += tabWidth;
3476 } else if (m[0] == "\r" || m[0] == "\n") {
3477 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3478 txt$1.setAttribute("cm-text", m[0]);
3479 builder.col += 1;
3480 } else {
3481 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3482 txt$1.setAttribute("cm-text", m[0]);
3483 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3484 else { content.appendChild(txt$1); }
3485 builder.col += 1;
3486 }
3487 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3488 builder.pos++;
3489 }
3490 }
3491 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3492 if (style || startStyle || endStyle || mustWrap || css) {
3493 var fullStyle = style || "";
3494 if (startStyle) { fullStyle += startStyle; }
3495 if (endStyle) { fullStyle += endStyle; }
3496 var token = elt("span", [content], fullStyle, css);
3497 if (title) { token.title = title; }
3498 return builder.content.appendChild(token)
3499 }
3500 builder.content.appendChild(content);
3501 }
3502
3503 function splitSpaces(text, trailingBefore) {
3504 if (text.length > 1 && !/ /.test(text)) { return text }
3505 var spaceBefore = trailingBefore, result = "";
3506 for (var i = 0; i < text.length; i++) {
3507 var ch = text.charAt(i);
3508 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3509 { ch = "\u00a0"; }
3510 result += ch;
3511 spaceBefore = ch == " ";
3512 }
3513 return result
3514 }
3515
3516 // Work around nonsense dimensions being reported for stretches of
3517 // right-to-left text.
3518 function buildTokenBadBidi(inner, order) {
3519 return function (builder, text, style, startStyle, endStyle, title, css) {
3520 style = style ? style + " cm-force-border" : "cm-force-border";
3521 var start = builder.pos, end = start + text.length;
3522 for (;;) {
3523 // Find the part that overlaps with the start of this text
3524 var part = (void 0);
3525 for (var i = 0; i < order.length; i++) {
3526 part = order[i];
3527 if (part.to > start && part.from <= start) { break }
3528 }
3529 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
3530 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
3531 startStyle = null;
3532 text = text.slice(part.to - start);
3533 start = part.to;
3534 }
3535 }
3536 }
3537
3538 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3539 var widget = !ignoreWidget && marker.widgetNode;
3540 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3541 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3542 if (!widget)
3543 { widget = builder.content.appendChild(document.createElement("span")); }
3544 widget.setAttribute("cm-marker", marker.id);
3545 }
3546 if (widget) {
3547 builder.cm.display.input.setUneditable(widget);
3548 builder.content.appendChild(widget);
3549 }
3550 builder.pos += size;
3551 builder.trailingSpace = false;
3552 }
3553
3554 // Outputs a number of spans to make up a line, taking highlighting
3555 // and marked text into account.
3556 function insertLineContent(line, builder, styles) {
3557 var spans = line.markedSpans, allText = line.text, at = 0;
3558 if (!spans) {
3559 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3560 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3561 return
3562 }
3563
3564 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3565 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
3566 for (;;) {
3567 if (nextChange == pos) { // Update current marker set
3568 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
3569 collapsed = null; nextChange = Infinity;
3570 var foundBookmarks = [], endStyles = (void 0);
3571 for (var j = 0; j < spans.length; ++j) {
3572 var sp = spans[j], m = sp.marker;
3573 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3574 foundBookmarks.push(m);
3575 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3576 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3577 nextChange = sp.to;
3578 spanEndStyle = "";
3579 }
3580 if (m.className) { spanStyle += " " + m.className; }
3581 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3582 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3583 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3584 if (m.title && !title) { title = m.title; }
3585 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3586 { collapsed = sp; }
3587 } else if (sp.from > pos && nextChange > sp.from) {
3588 nextChange = sp.from;
3589 }
3590 }
3591 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3592 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3593
3594 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3595 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3596 if (collapsed && (collapsed.from || 0) == pos) {
3597 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3598 collapsed.marker, collapsed.from == null);
3599 if (collapsed.to == null) { return }
3600 if (collapsed.to == pos) { collapsed = false; }
3601 }
3602 }
3603 if (pos >= len) { break }
3604
3605 var upto = Math.min(len, nextChange);
3606 while (true) {
3607 if (text) {
3608 var end = pos + text.length;
3609 if (!collapsed) {
3610 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3611 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3612 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
3613 }
3614 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3615 pos = end;
3616 spanStartStyle = "";
3617 }
3618 text = allText.slice(at, at = styles[i++]);
3619 style = interpretTokenStyle(styles[i++], builder.cm.options);
3620 }
3621 }
3622 }
3623
3624
3625 // These objects are used to represent the visible (currently drawn)
3626 // part of the document. A LineView may correspond to multiple
3627 // logical lines, if those are connected by collapsed ranges.
3628 function LineView(doc, line, lineN) {
3629 // The starting line
3630 this.line = line;
3631 // Continuing lines, if any
3632 this.rest = visualLineContinued(line);
3633 // Number of logical lines in this visual line
3634 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3635 this.node = this.text = null;
3636 this.hidden = lineIsHidden(doc, line);
3637 }
3638
3639 // Create a range of LineView objects for the given lines.
3640 function buildViewArray(cm, from, to) {
3641 var array = [], nextPos;
3642 for (var pos = from; pos < to; pos = nextPos) {
3643 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3644 nextPos = pos + view.size;
3645 array.push(view);
3646 }
3647 return array
3648 }
3649
3650 var operationGroup = null;
3651
3652 function pushOperation(op) {
3653 if (operationGroup) {
3654 operationGroup.ops.push(op);
3655 } else {
3656 op.ownsGroup = operationGroup = {
3657 ops: [op],
3658 delayedCallbacks: []
3659 };
3660 }
3661 }
3662
3663 function fireCallbacksForOps(group) {
3664 // Calls delayed callbacks and cursorActivity handlers until no
3665 // new ones appear
3666 var callbacks = group.delayedCallbacks, i = 0;
3667 do {
3668 for (; i < callbacks.length; i++)
3669 { callbacks[i].call(null); }
3670 for (var j = 0; j < group.ops.length; j++) {
3671 var op = group.ops[j];
3672 if (op.cursorActivityHandlers)
3673 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3674 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3675 }
3676 } while (i < callbacks.length)
3677 }
3678
3679 function finishOperation(op, endCb) {
3680 var group = op.ownsGroup;
3681 if (!group) { return }
3682
3683 try { fireCallbacksForOps(group); }
3684 finally {
3685 operationGroup = null;
3686 endCb(group);
3687 }
3688 }
3689
3690 var orphanDelayedCallbacks = null;
3691
3692 // Often, we want to signal events at a point where we are in the
3693 // middle of some work, but don't want the handler to start calling
3694 // other methods on the editor, which might be in an inconsistent
3695 // state or simply not expect any other events to happen.
3696 // signalLater looks whether there are any handlers, and schedules
3697 // them to be executed when the last operation ends, or, if no
3698 // operation is active, when a timeout fires.
3699 function signalLater(emitter, type /*, values...*/) {
3700 var arr = getHandlers(emitter, type);
3701 if (!arr.length) { return }
3702 var args = Array.prototype.slice.call(arguments, 2), list;
3703 if (operationGroup) {
3704 list = operationGroup.delayedCallbacks;
3705 } else if (orphanDelayedCallbacks) {
3706 list = orphanDelayedCallbacks;
3707 } else {
3708 list = orphanDelayedCallbacks = [];
3709 setTimeout(fireOrphanDelayed, 0);
3710 }
3711 var loop = function ( i ) {
3712 list.push(function () { return arr[i].apply(null, args); });
3713 };
3714
3715 for (var i = 0; i < arr.length; ++i)
3716 loop( i );
3717 }
3718
3719 function fireOrphanDelayed() {
3720 var delayed = orphanDelayedCallbacks;
3721 orphanDelayedCallbacks = null;
3722 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3723 }
3724
3725 // When an aspect of a line changes, a string is added to
3726 // lineView.changes. This updates the relevant part of the line's
3727 // DOM structure.
3728 function updateLineForChanges(cm, lineView, lineN, dims) {
3729 for (var j = 0; j < lineView.changes.length; j++) {
3730 var type = lineView.changes[j];
3731 if (type == "text") { updateLineText(cm, lineView); }
3732 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3733 else if (type == "class") { updateLineClasses(cm, lineView); }
3734 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3735 }
3736 lineView.changes = null;
3737 }
3738
3739 // Lines with gutter elements, widgets or a background class need to
3740 // be wrapped, and have the extra elements added to the wrapper div
3741 function ensureLineWrapped(lineView) {
3742 if (lineView.node == lineView.text) {
3743 lineView.node = elt("div", null, null, "position: relative");
3744 if (lineView.text.parentNode)
3745 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3746 lineView.node.appendChild(lineView.text);
3747 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3748 }
3749 return lineView.node
3750 }
3751
3752 function updateLineBackground(cm, lineView) {
3753 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3754 if (cls) { cls += " CodeMirror-linebackground"; }
3755 if (lineView.background) {
3756 if (cls) { lineView.background.className = cls; }
3757 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3758 } else if (cls) {
3759 var wrap = ensureLineWrapped(lineView);
3760 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3761 cm.display.input.setUneditable(lineView.background);
3762 }
3763 }
3764
3765 // Wrapper around buildLineContent which will reuse the structure
3766 // in display.externalMeasured when possible.
3767 function getLineContent(cm, lineView) {
3768 var ext = cm.display.externalMeasured;
3769 if (ext && ext.line == lineView.line) {
3770 cm.display.externalMeasured = null;
3771 lineView.measure = ext.measure;
3772 return ext.built
3773 }
3774 return buildLineContent(cm, lineView)
3775 }
3776
3777 // Redraw the line's text. Interacts with the background and text
3778 // classes because the mode may output tokens that influence these
3779 // classes.
3780 function updateLineText(cm, lineView) {
3781 var cls = lineView.text.className;
3782 var built = getLineContent(cm, lineView);
3783 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3784 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3785 lineView.text = built.pre;
3786 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3787 lineView.bgClass = built.bgClass;
3788 lineView.textClass = built.textClass;
3789 updateLineClasses(cm, lineView);
3790 } else if (cls) {
3791 lineView.text.className = cls;
3792 }
3793 }
3794
3795 function updateLineClasses(cm, lineView) {
3796 updateLineBackground(cm, lineView);
3797 if (lineView.line.wrapClass)
3798 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3799 else if (lineView.node != lineView.text)
3800 { lineView.node.className = ""; }
3801 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3802 lineView.text.className = textClass || "";
3803 }
3804
3805 function updateLineGutter(cm, lineView, lineN, dims) {
3806 if (lineView.gutter) {
3807 lineView.node.removeChild(lineView.gutter);
3808 lineView.gutter = null;
3809 }
3810 if (lineView.gutterBackground) {
3811 lineView.node.removeChild(lineView.gutterBackground);
3812 lineView.gutterBackground = null;
3813 }
3814 if (lineView.line.gutterClass) {
3815 var wrap = ensureLineWrapped(lineView);
3816 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3817 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3818 cm.display.input.setUneditable(lineView.gutterBackground);
3819 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3820 }
3821 var markers = lineView.line.gutterMarkers;
3822 if (cm.options.lineNumbers || markers) {
3823 var wrap$1 = ensureLineWrapped(lineView);
3824 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3825 cm.display.input.setUneditable(gutterWrap);
3826 wrap$1.insertBefore(gutterWrap, lineView.text);
3827 if (lineView.line.gutterClass)
3828 { gutterWrap.className += " " + lineView.line.gutterClass; }
3829 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3830 { lineView.lineNumber = gutterWrap.appendChild(
3831 elt("div", lineNumberFor(cm.options, lineN),
3832 "CodeMirror-linenumber CodeMirror-gutter-elt",
3833 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3834 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
3835 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3836 if (found)
3837 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3838 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3839 } }
3840 }
3841 }
3842
3843 function updateLineWidgets(cm, lineView, dims) {
3844 if (lineView.alignable) { lineView.alignable = null; }
3845 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3846 next = node.nextSibling;
3847 if (node.className == "CodeMirror-linewidget")
3848 { lineView.node.removeChild(node); }
3849 }
3850 insertLineWidgets(cm, lineView, dims);
3851 }
3852
3853 // Build a line's DOM representation from scratch
3854 function buildLineElement(cm, lineView, lineN, dims) {
3855 var built = getLineContent(cm, lineView);
3856 lineView.text = lineView.node = built.pre;
3857 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3858 if (built.textClass) { lineView.textClass = built.textClass; }
3859
3860 updateLineClasses(cm, lineView);
3861 updateLineGutter(cm, lineView, lineN, dims);
3862 insertLineWidgets(cm, lineView, dims);
3863 return lineView.node
3864 }
3865
3866 // A lineView may contain multiple logical lines (when merged by
3867 // collapsed spans). The widgets for all of them need to be drawn.
3868 function insertLineWidgets(cm, lineView, dims) {
3869 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3870 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3871 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3872 }
3873
3874 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3875 if (!line.widgets) { return }
3876 var wrap = ensureLineWrapped(lineView);
3877 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3878 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3879 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3880 positionLineWidget(widget, node, lineView, dims);
3881 cm.display.input.setUneditable(node);
3882 if (allowAbove && widget.above)
3883 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3884 else
3885 { wrap.appendChild(node); }
3886 signalLater(widget, "redraw");
3887 }
3888 }
3889
3890 function positionLineWidget(widget, node, lineView, dims) {
3891 if (widget.noHScroll) {
3892 (lineView.alignable || (lineView.alignable = [])).push(node);
3893 var width = dims.wrapperWidth;
3894 node.style.left = dims.fixedPos + "px";
3895 if (!widget.coverGutter) {
3896 width -= dims.gutterTotalWidth;
3897 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3898 }
3899 node.style.width = width + "px";
3900 }
3901 if (widget.coverGutter) {
3902 node.style.zIndex = 5;
3903 node.style.position = "relative";
3904 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3905 }
3906 }
3907
3908 function widgetHeight(widget) {
3909 if (widget.height != null) { return widget.height }
3910 var cm = widget.doc.cm;
3911 if (!cm) { return 0 }
3912 if (!contains(document.body, widget.node)) {
3913 var parentStyle = "position: relative;";
3914 if (widget.coverGutter)
3915 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3916 if (widget.noHScroll)
3917 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3918 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3919 }
3920 return widget.height = widget.node.parentNode.offsetHeight
3921 }
3922
3923 // Return true when the given mouse event happened in a widget
3924 function eventInWidget(display, e) {
3925 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3926 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3927 (n.parentNode == display.sizer && n != display.mover))
3928 { return true }
3929 }
3930 }
3931
3932 // POSITION MEASUREMENT
3933
3934 function paddingTop(display) {return display.lineSpace.offsetTop}
3935 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3936 function paddingH(display) {
3937 if (display.cachedPaddingH) { return display.cachedPaddingH }
3938 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3939 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3940 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3941 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3942 return data
3943 }
3944
3945 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3946 function displayWidth(cm) {
3947 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3948 }
3949 function displayHeight(cm) {
3950 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3951 }
3952
3953 // Ensure the lineView.wrapping.heights array is populated. This is
3954 // an array of bottom offsets for the lines that make up a drawn
3955 // line. When lineWrapping is on, there might be more than one
3956 // height.
3957 function ensureLineHeights(cm, lineView, rect) {
3958 var wrapping = cm.options.lineWrapping;
3959 var curWidth = wrapping && displayWidth(cm);
3960 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3961 var heights = lineView.measure.heights = [];
3962 if (wrapping) {
3963 lineView.measure.width = curWidth;
3964 var rects = lineView.text.firstChild.getClientRects();
3965 for (var i = 0; i < rects.length - 1; i++) {
3966 var cur = rects[i], next = rects[i + 1];
3967 if (Math.abs(cur.bottom - next.bottom) > 2)
3968 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
3969 }
3970 }
3971 heights.push(rect.bottom - rect.top);
3972 }
3973 }
3974
3975 // Find a line map (mapping character offsets to text nodes) and a
3976 // measurement cache for the given line number. (A line view might
3977 // contain multiple lines when collapsed ranges are present.)
3978 function mapFromLineView(lineView, line, lineN) {
3979 if (lineView.line == line)
3980 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
3981 for (var i = 0; i < lineView.rest.length; i++)
3982 { if (lineView.rest[i] == line)
3983 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
3984 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
3985 { if (lineNo(lineView.rest[i$1]) > lineN)
3986 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
3987 }
3988
3989 // Render a line into the hidden node display.externalMeasured. Used
3990 // when measurement is needed for a line that's not in the viewport.
3991 function updateExternalMeasurement(cm, line) {
3992 line = visualLine(line);
3993 var lineN = lineNo(line);
3994 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
3995 view.lineN = lineN;
3996 var built = view.built = buildLineContent(cm, view);
3997 view.text = built.pre;
3998 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
3999 return view
4000 }
4001
4002 // Get a {top, bottom, left, right} box (in line-local coordinates)
4003 // for a given character.
4004 function measureChar(cm, line, ch, bias) {
4005 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
4006 }
4007
4008 // Find a line view that corresponds to the given line number.
4009 function findViewForLine(cm, lineN) {
4010 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
4011 { return cm.display.view[findViewIndex(cm, lineN)] }
4012 var ext = cm.display.externalMeasured;
4013 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
4014 { return ext }
4015 }
4016
4017 // Measurement can be split in two steps, the set-up work that
4018 // applies to the whole line, and the measurement of the actual
4019 // character. Functions like coordsChar, that need to do a lot of
4020 // measurements in a row, can thus ensure that the set-up work is
4021 // only done once.
4022 function prepareMeasureForLine(cm, line) {
4023 var lineN = lineNo(line);
4024 var view = findViewForLine(cm, lineN);
4025 if (view && !view.text) {
4026 view = null;
4027 } else if (view && view.changes) {
4028 updateLineForChanges(cm, view, lineN, getDimensions(cm));
4029 cm.curOp.forceUpdate = true;
4030 }
4031 if (!view)
4032 { view = updateExternalMeasurement(cm, line); }
4033
4034 var info = mapFromLineView(view, line, lineN);
4035 return {
4036 line: line, view: view, rect: null,
4037 map: info.map, cache: info.cache, before: info.before,
4038 hasHeights: false
4039 }
4040 }
4041
4042 // Given a prepared measurement object, measures the position of an
4043 // actual character (or fetches it from the cache).
4044 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
4045 if (prepared.before) { ch = -1; }
4046 var key = ch + (bias || ""), found;
4047 if (prepared.cache.hasOwnProperty(key)) {
4048 found = prepared.cache[key];
4049 } else {
4050 if (!prepared.rect)
4051 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
4052 if (!prepared.hasHeights) {
4053 ensureLineHeights(cm, prepared.view, prepared.rect);
4054 prepared.hasHeights = true;
4055 }
4056 found = measureCharInner(cm, prepared, ch, bias);
4057 if (!found.bogus) { prepared.cache[key] = found; }
4058 }
4059 return {left: found.left, right: found.right,
4060 top: varHeight ? found.rtop : found.top,
4061 bottom: varHeight ? found.rbottom : found.bottom}
4062 }
4063
4064 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4065
4066 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
4067 var node, start, end, collapse, mStart, mEnd;
4068 // First, search the line map for the text node corresponding to,
4069 // or closest to, the target character.
4070 for (var i = 0; i < map$$1.length; i += 3) {
4071 mStart = map$$1[i];
4072 mEnd = map$$1[i + 1];
4073 if (ch < mStart) {
4074 start = 0; end = 1;
4075 collapse = "left";
4076 } else if (ch < mEnd) {
4077 start = ch - mStart;
4078 end = start + 1;
4079 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
4080 end = mEnd - mStart;
4081 start = end - 1;
4082 if (ch >= mEnd) { collapse = "right"; }
4083 }
4084 if (start != null) {
4085 node = map$$1[i + 2];
4086 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4087 { collapse = bias; }
4088 if (bias == "left" && start == 0)
4089 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
4090 node = map$$1[(i -= 3) + 2];
4091 collapse = "left";
4092 } }
4093 if (bias == "right" && start == mEnd - mStart)
4094 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
4095 node = map$$1[(i += 3) + 2];
4096 collapse = "right";
4097 } }
4098 break
4099 }
4100 }
4101 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4102 }
4103
4104 function getUsefulRect(rects, bias) {
4105 var rect = nullRect;
4106 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4107 if ((rect = rects[i]).left != rect.right) { break }
4108 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4109 if ((rect = rects[i$1]).left != rect.right) { break }
4110 } }
4111 return rect
4112 }
4113
4114 function measureCharInner(cm, prepared, ch, bias) {
4115 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4116 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4117
4118 var rect;
4119 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4120 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4121 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4122 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4123 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4124 { rect = node.parentNode.getBoundingClientRect(); }
4125 else
4126 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4127 if (rect.left || rect.right || start == 0) { break }
4128 end = start;
4129 start = start - 1;
4130 collapse = "right";
4131 }
4132 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4133 } else { // If it is a widget, simply get the box for the whole widget.
4134 if (start > 0) { collapse = bias = "right"; }
4135 var rects;
4136 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4137 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4138 else
4139 { rect = node.getBoundingClientRect(); }
4140 }
4141 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4142 var rSpan = node.parentNode.getClientRects()[0];
4143 if (rSpan)
4144 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4145 else
4146 { rect = nullRect; }
4147 }
4148
4149 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4150 var mid = (rtop + rbot) / 2;
4151 var heights = prepared.view.measure.heights;
4152 var i = 0;
4153 for (; i < heights.length - 1; i++)
4154 { if (mid < heights[i]) { break } }
4155 var top = i ? heights[i - 1] : 0, bot = heights[i];
4156 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4157 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4158 top: top, bottom: bot};
4159 if (!rect.left && !rect.right) { result.bogus = true; }
4160 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4161
4162 return result
4163 }
4164
4165 // Work around problem with bounding client rects on ranges being
4166 // returned incorrectly when zoomed on IE10 and below.
4167 function maybeUpdateRectForZooming(measure, rect) {
4168 if (!window.screen || screen.logicalXDPI == null ||
4169 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4170 { return rect }
4171 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4172 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4173 return {left: rect.left * scaleX, right: rect.right * scaleX,
4174 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4175 }
4176
4177 function clearLineMeasurementCacheFor(lineView) {
4178 if (lineView.measure) {
4179 lineView.measure.cache = {};
4180 lineView.measure.heights = null;
4181 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4182 { lineView.measure.caches[i] = {}; } }
4183 }
4184 }
4185
4186 function clearLineMeasurementCache(cm) {
4187 cm.display.externalMeasure = null;
4188 removeChildren(cm.display.lineMeasure);
4189 for (var i = 0; i < cm.display.view.length; i++)
4190 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4191 }
4192
4193 function clearCaches(cm) {
4194 clearLineMeasurementCache(cm);
4195 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4196 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4197 cm.display.lineNumChars = null;
4198 }
4199
4200 function pageScrollX() {
4201 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4202 // which causes page_Offset and bounding client rects to use
4203 // different reference viewports and invalidate our calculations.
4204 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4205 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4206 }
4207 function pageScrollY() {
4208 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4209 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4210 }
4211
4212 function widgetTopHeight(lineObj) {
4213 var height = 0;
4214 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4215 { height += widgetHeight(lineObj.widgets[i]); } } }
4216 return height
4217 }
4218
4219 // Converts a {top, bottom, left, right} box from line-local
4220 // coordinates into another coordinate system. Context may be one of
4221 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4222 // or "page".
4223 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4224 if (!includeWidgets) {
4225 var height = widgetTopHeight(lineObj);
4226 rect.top += height; rect.bottom += height;
4227 }
4228 if (context == "line") { return rect }
4229 if (!context) { context = "local"; }
4230 var yOff = heightAtLine(lineObj);
4231 if (context == "local") { yOff += paddingTop(cm.display); }
4232 else { yOff -= cm.display.viewOffset; }
4233 if (context == "page" || context == "window") {
4234 var lOff = cm.display.lineSpace.getBoundingClientRect();
4235 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4236 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4237 rect.left += xOff; rect.right += xOff;
4238 }
4239 rect.top += yOff; rect.bottom += yOff;
4240 return rect
4241 }
4242
4243 // Coverts a box from "div" coords to another coordinate system.
4244 // Context may be "window", "page", "div", or "local"./null.
4245 function fromCoordSystem(cm, coords, context) {
4246 if (context == "div") { return coords }
4247 var left = coords.left, top = coords.top;
4248 // First move into "page" coordinate system
4249 if (context == "page") {
4250 left -= pageScrollX();
4251 top -= pageScrollY();
4252 } else if (context == "local" || !context) {
4253 var localBox = cm.display.sizer.getBoundingClientRect();
4254 left += localBox.left;
4255 top += localBox.top;
4256 }
4257
4258 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4259 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4260 }
4261
4262 function charCoords(cm, pos, context, lineObj, bias) {
4263 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4264 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4265 }
4266
4267 // Returns a box for a given cursor position, which may have an
4268 // 'other' property containing the position of the secondary cursor
4269 // on a bidi boundary.
4270 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4271 // and after `char - 1` in writing order of `char - 1`
4272 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4273 // and before `char` in writing order of `char`
4274 // Examples (upper-case letters are RTL, lower-case are LTR):
4275 // Pos(0, 1, ...)
4276 // before after
4277 // ab a|b a|b
4278 // aB a|B aB|
4279 // Ab |Ab A|b
4280 // AB B|A B|A
4281 // Every position after the last character on a line is considered to stick
4282 // to the last character on the line.
4283 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4284 lineObj = lineObj || getLine(cm.doc, pos.line);
4285 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4286 function get(ch, right) {
4287 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4288 if (right) { m.left = m.right; } else { m.right = m.left; }
4289 return intoCoordSystem(cm, lineObj, m, context)
4290 }
4291 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4292 if (ch >= lineObj.text.length) {
4293 ch = lineObj.text.length;
4294 sticky = "before";
4295 } else if (ch <= 0) {
4296 ch = 0;
4297 sticky = "after";
4298 }
4299 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4300
4301 function getBidi(ch, partPos, invert) {
4302 var part = order[partPos], right = part.level == 1;
4303 return get(invert ? ch - 1 : ch, right != invert)
4304 }
4305 var partPos = getBidiPartAt(order, ch, sticky);
4306 var other = bidiOther;
4307 var val = getBidi(ch, partPos, sticky == "before");
4308 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4309 return val
4310 }
4311
4312 // Used to cheaply estimate the coordinates for a position. Used for
4313 // intermediate scroll updates.
4314 function estimateCoords(cm, pos) {
4315 var left = 0;
4316 pos = clipPos(cm.doc, pos);
4317 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4318 var lineObj = getLine(cm.doc, pos.line);
4319 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4320 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4321 }
4322
4323 // Positions returned by coordsChar contain some extra information.
4324 // xRel is the relative x position of the input coordinates compared
4325 // to the found position (so xRel > 0 means the coordinates are to
4326 // the right of the character position, for example). When outside
4327 // is true, that means the coordinates lie outside the line's
4328 // vertical range.
4329 function PosWithInfo(line, ch, sticky, outside, xRel) {
4330 var pos = Pos(line, ch, sticky);
4331 pos.xRel = xRel;
4332 if (outside) { pos.outside = true; }
4333 return pos
4334 }
4335
4336 // Compute the character position closest to the given coordinates.
4337 // Input must be lineSpace-local ("div" coordinate system).
4338 function coordsChar(cm, x, y) {
4339 var doc = cm.doc;
4340 y += cm.display.viewOffset;
4341 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4342 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4343 if (lineN > last)
4344 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4345 if (x < 0) { x = 0; }
4346
4347 var lineObj = getLine(doc, lineN);
4348 for (;;) {
4349 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4350 var merged = collapsedSpanAtEnd(lineObj);
4351 var mergedPos = merged && merged.find(0, true);
4352 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
4353 { lineN = lineNo(lineObj = mergedPos.to.line); }
4354 else
4355 { return found }
4356 }
4357 }
4358
4359 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4360 y -= widgetTopHeight(lineObj);
4361 var end = lineObj.text.length;
4362 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4363 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4364 return {begin: begin, end: end}
4365 }
4366
4367 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4368 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4369 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4370 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4371 }
4372
4373 // Returns true if the given side of a box is after the given
4374 // coordinates, in top-to-bottom, left-to-right order.
4375 function boxIsAfter(box, x, y, left) {
4376 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4377 }
4378
4379 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4380 // Move y into line-local coordinate space
4381 y -= heightAtLine(lineObj);
4382 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4383 // When directly calling `measureCharPrepared`, we have to adjust
4384 // for the widgets at this line.
4385 var widgetHeight$$1 = widgetTopHeight(lineObj);
4386 var begin = 0, end = lineObj.text.length, ltr = true;
4387
4388 var order = getOrder(lineObj, cm.doc.direction);
4389 // If the line isn't plain left-to-right text, first figure out
4390 // which bidi section the coordinates fall into.
4391 if (order) {
4392 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4393 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
4394 ltr = part.level != 1;
4395 // The awkward -1 offsets are needed because findFirst (called
4396 // on these below) will treat its first bound as inclusive,
4397 // second as exclusive, but we want to actually address the
4398 // characters in the part's range
4399 begin = ltr ? part.from : part.to - 1;
4400 end = ltr ? part.to : part.from - 1;
4401 }
4402
4403 // A binary search to find the first character whose bounding box
4404 // starts after the coordinates. If we run across any whose box wrap
4405 // the coordinates, store that.
4406 var chAround = null, boxAround = null;
4407 var ch = findFirst(function (ch) {
4408 var box = measureCharPrepared(cm, preparedMeasure, ch);
4409 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
4410 if (!boxIsAfter(box, x, y, false)) { return false }
4411 if (box.top <= y && box.left <= x) {
4412 chAround = ch;
4413 boxAround = box;
4414 }
4415 return true
4416 }, begin, end);
4417
4418 var baseX, sticky, outside = false;
4419 // If a box around the coordinates was found, use that
4420 if (boxAround) {
4421 // Distinguish coordinates nearer to the left or right side of the box
4422 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4423 ch = chAround + (atStart ? 0 : 1);
4424 sticky = atStart ? "after" : "before";
4425 baseX = atLeft ? boxAround.left : boxAround.right;
4426 } else {
4427 // (Adjust for extended bound, if necessary.)
4428 if (!ltr && (ch == end || ch == begin)) { ch++; }
4429 // To determine which side to associate with, get the box to the
4430 // left of the character and compare it's vertical position to the
4431 // coordinates
4432 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4433 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
4434 "after" : "before";
4435 // Now get accurate coordinates for this place, in order to get a
4436 // base X position
4437 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
4438 baseX = coords.left;
4439 outside = y < coords.top || y >= coords.bottom;
4440 }
4441
4442 ch = skipExtendingChars(lineObj.text, ch, 1);
4443 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
4444 }
4445
4446 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
4447 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4448 // situation, we can take this ordering to correspond to the visual
4449 // ordering. This finds the first part whose end is after the given
4450 // coordinates.
4451 var index = findFirst(function (i) {
4452 var part = order[i], ltr = part.level != 1;
4453 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
4454 "line", lineObj, preparedMeasure), x, y, true)
4455 }, 0, order.length - 1);
4456 var part = order[index];
4457 // If this isn't the first part, the part's start is also after
4458 // the coordinates, and the coordinates aren't on the same line as
4459 // that start, move one part back.
4460 if (index > 0) {
4461 var ltr = part.level != 1;
4462 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
4463 "line", lineObj, preparedMeasure);
4464 if (boxIsAfter(start, x, y, true) && start.top > y)
4465 { part = order[index - 1]; }
4466 }
4467 return part
4468 }
4469
4470 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4471 // In a wrapped line, rtl text on wrapping boundaries can do things
4472 // that don't correspond to the ordering in our `order` array at
4473 // all, so a binary search doesn't work, and we want to return a
4474 // part that only spans one line so that the binary search in
4475 // coordsCharInner is safe. As such, we first find the extent of the
4476 // wrapped line, and then do a flat search in which we discard any
4477 // spans that aren't on the line.
4478 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4479 var begin = ref.begin;
4480 var end = ref.end;
4481 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4482 var part = null, closestDist = null;
4483 for (var i = 0; i < order.length; i++) {
4484 var p = order[i];
4485 if (p.from >= end || p.to <= begin) { continue }
4486 var ltr = p.level != 1;
4487 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4488 // Weigh against spans ending before this, so that they are only
4489 // picked if nothing ends after
4490 var dist = endX < x ? x - endX + 1e9 : endX - x;
4491 if (!part || closestDist > dist) {
4492 part = p;
4493 closestDist = dist;
4494 }
4495 }
4496 if (!part) { part = order[order.length - 1]; }
4497 // Clip the part to the wrapped line.
4498 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4499 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4500 return part
4501 }
4502
4503 var measureText;
4504 // Compute the default text height.
4505 function textHeight(display) {
4506 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4507 if (measureText == null) {
4508 measureText = elt("pre");
4509 // Measure a bunch of lines, for browsers that compute
4510 // fractional heights.
4511 for (var i = 0; i < 49; ++i) {
4512 measureText.appendChild(document.createTextNode("x"));
4513 measureText.appendChild(elt("br"));
4514 }
4515 measureText.appendChild(document.createTextNode("x"));
4516 }
4517 removeChildrenAndAdd(display.measure, measureText);
4518 var height = measureText.offsetHeight / 50;
4519 if (height > 3) { display.cachedTextHeight = height; }
4520 removeChildren(display.measure);
4521 return height || 1
4522 }
4523
4524 // Compute the default character width.
4525 function charWidth(display) {
4526 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4527 var anchor = elt("span", "xxxxxxxxxx");
4528 var pre = elt("pre", [anchor]);
4529 removeChildrenAndAdd(display.measure, pre);
4530 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4531 if (width > 2) { display.cachedCharWidth = width; }
4532 return width || 10
4533 }
4534
4535 // Do a bulk-read of the DOM positions and sizes needed to draw the
4536 // view, so that we don't interleave reading and writing to the DOM.
4537 function getDimensions(cm) {
4538 var d = cm.display, left = {}, width = {};
4539 var gutterLeft = d.gutters.clientLeft;
4540 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4541 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
4542 width[cm.options.gutters[i]] = n.clientWidth;
4543 }
4544 return {fixedPos: compensateForHScroll(d),
4545 gutterTotalWidth: d.gutters.offsetWidth,
4546 gutterLeft: left,
4547 gutterWidth: width,
4548 wrapperWidth: d.wrapper.clientWidth}
4549 }
4550
4551 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4552 // but using getBoundingClientRect to get a sub-pixel-accurate
4553 // result.
4554 function compensateForHScroll(display) {
4555 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4556 }
4557
4558 // Returns a function that estimates the height of a line, to use as
4559 // first approximation until the line becomes visible (and is thus
4560 // properly measurable).
4561 function estimateHeight(cm) {
4562 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4563 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4564 return function (line) {
4565 if (lineIsHidden(cm.doc, line)) { return 0 }
4566
4567 var widgetsHeight = 0;
4568 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4569 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4570 } }
4571
4572 if (wrapping)
4573 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4574 else
4575 { return widgetsHeight + th }
4576 }
4577 }
4578
4579 function estimateLineHeights(cm) {
4580 var doc = cm.doc, est = estimateHeight(cm);
4581 doc.iter(function (line) {
4582 var estHeight = est(line);
4583 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4584 });
4585 }
4586
4587 // Given a mouse event, find the corresponding position. If liberal
4588 // is false, it checks whether a gutter or scrollbar was clicked,
4589 // and returns null if it was. forRect is used by rectangular
4590 // selections, and tries to estimate a character position even for
4591 // coordinates beyond the right of the text.
4592 function posFromMouse(cm, e, liberal, forRect) {
4593 var display = cm.display;
4594 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4595
4596 var x, y, space = display.lineSpace.getBoundingClientRect();
4597 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4598 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4599 catch (e) { return null }
4600 var coords = coordsChar(cm, x, y), line;
4601 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4602 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4603 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4604 }
4605 return coords
4606 }
4607
4608 // Find the view element corresponding to a given line. Return null
4609 // when the line isn't visible.
4610 function findViewIndex(cm, n) {
4611 if (n >= cm.display.viewTo) { return null }
4612 n -= cm.display.viewFrom;
4613 if (n < 0) { return null }
4614 var view = cm.display.view;
4615 for (var i = 0; i < view.length; i++) {
4616 n -= view[i].size;
4617 if (n < 0) { return i }
4618 }
4619 }
4620
4621 function updateSelection(cm) {
4622 cm.display.input.showSelection(cm.display.input.prepareSelection());
4623 }
4624
4625 function prepareSelection(cm, primary) {
4626 if ( primary === void 0 ) primary = true;
4627
4628 var doc = cm.doc, result = {};
4629 var curFragment = result.cursors = document.createDocumentFragment();
4630 var selFragment = result.selection = document.createDocumentFragment();
4631
4632 for (var i = 0; i < doc.sel.ranges.length; i++) {
4633 if (!primary && i == doc.sel.primIndex) { continue }
4634 var range$$1 = doc.sel.ranges[i];
4635 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4636 var collapsed = range$$1.empty();
4637 if (collapsed || cm.options.showCursorWhenSelecting)
4638 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4639 if (!collapsed)
4640 { drawSelectionRange(cm, range$$1, selFragment); }
4641 }
4642 return result
4643 }
4644
4645 // Draws a cursor for the given range
4646 function drawSelectionCursor(cm, head, output) {
4647 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4648
4649 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4650 cursor.style.left = pos.left + "px";
4651 cursor.style.top = pos.top + "px";
4652 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4653
4654 if (pos.other) {
4655 // Secondary cursor, shown when on a 'jump' in bi-directional text
4656 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4657 otherCursor.style.display = "";
4658 otherCursor.style.left = pos.other.left + "px";
4659 otherCursor.style.top = pos.other.top + "px";
4660 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4661 }
4662 }
4663
4664 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4665
4666 // Draws the given range as a highlighted selection
4667 function drawSelectionRange(cm, range$$1, output) {
4668 var display = cm.display, doc = cm.doc;
4669 var fragment = document.createDocumentFragment();
4670 var padding = paddingH(cm.display), leftSide = padding.left;
4671 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4672 var docLTR = doc.direction == "ltr";
4673
4674 function add(left, top, width, bottom) {
4675 if (top < 0) { top = 0; }
4676 top = Math.round(top);
4677 bottom = Math.round(bottom);
4678 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")));
4679 }
4680
4681 function drawForLine(line, fromArg, toArg) {
4682 var lineObj = getLine(doc, line);
4683 var lineLen = lineObj.text.length;
4684 var start, end;
4685 function coords(ch, bias) {
4686 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4687 }
4688
4689 function wrapX(pos, dir, side) {
4690 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4691 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4692 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4693 return coords(ch, prop)[prop]
4694 }
4695
4696 var order = getOrder(lineObj, doc.direction);
4697 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4698 var ltr = dir == "ltr";
4699 var fromPos = coords(from, ltr ? "left" : "right");
4700 var toPos = coords(to - 1, ltr ? "right" : "left");
4701
4702 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4703 var first = i == 0, last = !order || i == order.length - 1;
4704 if (toPos.top - fromPos.top <= 3) { // Single line
4705 var openLeft = (docLTR ? openStart : openEnd) && first;
4706 var openRight = (docLTR ? openEnd : openStart) && last;
4707 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4708 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4709 add(left, fromPos.top, right - left, fromPos.bottom);
4710 } else { // Multiple lines
4711 var topLeft, topRight, botLeft, botRight;
4712 if (ltr) {
4713 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4714 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4715 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4716 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4717 } else {
4718 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4719 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4720 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4721 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4722 }
4723 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4724 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4725 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4726 }
4727
4728 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4729 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4730 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4731 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4732 });
4733 return {start: start, end: end}
4734 }
4735
4736 var sFrom = range$$1.from(), sTo = range$$1.to();
4737 if (sFrom.line == sTo.line) {
4738 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4739 } else {
4740 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4741 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4742 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4743 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4744 if (singleVLine) {
4745 if (leftEnd.top < rightStart.top - 2) {
4746 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4747 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4748 } else {
4749 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4750 }
4751 }
4752 if (leftEnd.bottom < rightStart.top)
4753 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4754 }
4755
4756 output.appendChild(fragment);
4757 }
4758
4759 // Cursor-blinking
4760 function restartBlink(cm) {
4761 if (!cm.state.focused) { return }
4762 var display = cm.display;
4763 clearInterval(display.blinker);
4764 var on = true;
4765 display.cursorDiv.style.visibility = "";
4766 if (cm.options.cursorBlinkRate > 0)
4767 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4768 cm.options.cursorBlinkRate); }
4769 else if (cm.options.cursorBlinkRate < 0)
4770 { display.cursorDiv.style.visibility = "hidden"; }
4771 }
4772
4773 function ensureFocus(cm) {
4774 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4775 }
4776
4777 function delayBlurEvent(cm) {
4778 cm.state.delayingBlurEvent = true;
4779 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4780 cm.state.delayingBlurEvent = false;
4781 onBlur(cm);
4782 } }, 100);
4783 }
4784
4785 function onFocus(cm, e) {
4786 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4787
4788 if (cm.options.readOnly == "nocursor") { return }
4789 if (!cm.state.focused) {
4790 signal(cm, "focus", cm, e);
4791 cm.state.focused = true;
4792 addClass(cm.display.wrapper, "CodeMirror-focused");
4793 // This test prevents this from firing when a context
4794 // menu is closed (since the input reset would kill the
4795 // select-all detection hack)
4796 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4797 cm.display.input.reset();
4798 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4799 }
4800 cm.display.input.receivedFocus();
4801 }
4802 restartBlink(cm);
4803 }
4804 function onBlur(cm, e) {
4805 if (cm.state.delayingBlurEvent) { return }
4806
4807 if (cm.state.focused) {
4808 signal(cm, "blur", cm, e);
4809 cm.state.focused = false;
4810 rmClass(cm.display.wrapper, "CodeMirror-focused");
4811 }
4812 clearInterval(cm.display.blinker);
4813 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4814 }
4815
4816 // Read the actual heights of the rendered lines, and update their
4817 // stored heights to match.
4818 function updateHeightsInViewport(cm) {
4819 var display = cm.display;
4820 var prevBottom = display.lineDiv.offsetTop;
4821 for (var i = 0; i < display.view.length; i++) {
4822 var cur = display.view[i], height = (void 0);
4823 if (cur.hidden) { continue }
4824 if (ie && ie_version < 8) {
4825 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4826 height = bot - prevBottom;
4827 prevBottom = bot;
4828 } else {
4829 var box = cur.node.getBoundingClientRect();
4830 height = box.bottom - box.top;
4831 }
4832 var diff = cur.line.height - height;
4833 if (height < 2) { height = textHeight(display); }
4834 if (diff > .005 || diff < -.005) {
4835 updateLineHeight(cur.line, height);
4836 updateWidgetHeight(cur.line);
4837 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4838 { updateWidgetHeight(cur.rest[j]); } }
4839 }
4840 }
4841 }
4842
4843 // Read and store the height of line widgets associated with the
4844 // given line.
4845 function updateWidgetHeight(line) {
4846 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4847 var w = line.widgets[i], parent = w.node.parentNode;
4848 if (parent) { w.height = parent.offsetHeight; }
4849 } }
4850 }
4851
4852 // Compute the lines that are visible in a given viewport (defaults
4853 // the the current scroll position). viewport may contain top,
4854 // height, and ensure (see op.scrollToPos) properties.
4855 function visibleLines(display, doc, viewport) {
4856 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4857 top = Math.floor(top - paddingTop(display));
4858 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4859
4860 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4861 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4862 // forces those lines into the viewport (if possible).
4863 if (viewport && viewport.ensure) {
4864 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4865 if (ensureFrom < from) {
4866 from = ensureFrom;
4867 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4868 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4869 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4870 to = ensureTo;
4871 }
4872 }
4873 return {from: from, to: Math.max(to, from + 1)}
4874 }
4875
4876 // Re-align line numbers and gutter marks to compensate for
4877 // horizontal scrolling.
4878 function alignHorizontally(cm) {
4879 var display = cm.display, view = display.view;
4880 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4881 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4882 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4883 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4884 if (cm.options.fixedGutter) {
4885 if (view[i].gutter)
4886 { view[i].gutter.style.left = left; }
4887 if (view[i].gutterBackground)
4888 { view[i].gutterBackground.style.left = left; }
4889 }
4890 var align = view[i].alignable;
4891 if (align) { for (var j = 0; j < align.length; j++)
4892 { align[j].style.left = left; } }
4893 } }
4894 if (cm.options.fixedGutter)
4895 { display.gutters.style.left = (comp + gutterW) + "px"; }
4896 }
4897
4898 // Used to ensure that the line number gutter is still the right
4899 // size for the current document size. Returns true when an update
4900 // is needed.
4901 function maybeUpdateLineNumberWidth(cm) {
4902 if (!cm.options.lineNumbers) { return false }
4903 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4904 if (last.length != display.lineNumChars) {
4905 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4906 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4907 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4908 display.lineGutter.style.width = "";
4909 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4910 display.lineNumWidth = display.lineNumInnerWidth + padding;
4911 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4912 display.lineGutter.style.width = display.lineNumWidth + "px";
4913 updateGutterSpace(cm);
4914 return true
4915 }
4916 return false
4917 }
4918
4919 // SCROLLING THINGS INTO VIEW
4920
4921 // If an editor sits on the top or bottom of the window, partially
4922 // scrolled out of view, this ensures that the cursor is visible.
4923 function maybeScrollWindow(cm, rect) {
4924 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4925
4926 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4927 if (rect.top + box.top < 0) { doScroll = true; }
4928 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4929 if (doScroll != null && !phantom) {
4930 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;"));
4931 cm.display.lineSpace.appendChild(scrollNode);
4932 scrollNode.scrollIntoView(doScroll);
4933 cm.display.lineSpace.removeChild(scrollNode);
4934 }
4935 }
4936
4937 // Scroll a given position into view (immediately), verifying that
4938 // it actually became visible (as line heights are accurately
4939 // measured, the position of something may 'drift' during drawing).
4940 function scrollPosIntoView(cm, pos, end, margin) {
4941 if (margin == null) { margin = 0; }
4942 var rect;
4943 if (!cm.options.lineWrapping && pos == end) {
4944 // Set pos and end to the cursor positions around the character pos sticks to
4945 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4946 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4947 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
4948 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
4949 }
4950 for (var limit = 0; limit < 5; limit++) {
4951 var changed = false;
4952 var coords = cursorCoords(cm, pos);
4953 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4954 rect = {left: Math.min(coords.left, endCoords.left),
4955 top: Math.min(coords.top, endCoords.top) - margin,
4956 right: Math.max(coords.left, endCoords.left),
4957 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
4958 var scrollPos = calculateScrollPos(cm, rect);
4959 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4960 if (scrollPos.scrollTop != null) {
4961 updateScrollTop(cm, scrollPos.scrollTop);
4962 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
4963 }
4964 if (scrollPos.scrollLeft != null) {
4965 setScrollLeft(cm, scrollPos.scrollLeft);
4966 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
4967 }
4968 if (!changed) { break }
4969 }
4970 return rect
4971 }
4972
4973 // Scroll a given set of coordinates into view (immediately).
4974 function scrollIntoView(cm, rect) {
4975 var scrollPos = calculateScrollPos(cm, rect);
4976 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
4977 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
4978 }
4979
4980 // Calculate a new scroll position needed to scroll the given
4981 // rectangle into view. Returns an object with scrollTop and
4982 // scrollLeft properties. When these are undefined, the
4983 // vertical/horizontal position does not need to be adjusted.
4984 function calculateScrollPos(cm, rect) {
4985 var display = cm.display, snapMargin = textHeight(cm.display);
4986 if (rect.top < 0) { rect.top = 0; }
4987 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4988 var screen = displayHeight(cm), result = {};
4989 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
4990 var docBottom = cm.doc.height + paddingVert(display);
4991 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
4992 if (rect.top < screentop) {
4993 result.scrollTop = atTop ? 0 : rect.top;
4994 } else if (rect.bottom > screentop + screen) {
4995 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
4996 if (newTop != screentop) { result.scrollTop = newTop; }
4997 }
4998
4999 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
5000 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
5001 var tooWide = rect.right - rect.left > screenw;
5002 if (tooWide) { rect.right = rect.left + screenw; }
5003 if (rect.left < 10)
5004 { result.scrollLeft = 0; }
5005 else if (rect.left < screenleft)
5006 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
5007 else if (rect.right > screenw + screenleft - 3)
5008 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
5009 return result
5010 }
5011
5012 // Store a relative adjustment to the scroll position in the current
5013 // operation (to be applied when the operation finishes).
5014 function addToScrollTop(cm, top) {
5015 if (top == null) { return }
5016 resolveScrollToPos(cm);
5017 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
5018 }
5019
5020 // Make sure that at the end of the operation the current cursor is
5021 // shown.
5022 function ensureCursorVisible(cm) {
5023 resolveScrollToPos(cm);
5024 var cur = cm.getCursor();
5025 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
5026 }
5027
5028 function scrollToCoords(cm, x, y) {
5029 if (x != null || y != null) { resolveScrollToPos(cm); }
5030 if (x != null) { cm.curOp.scrollLeft = x; }
5031 if (y != null) { cm.curOp.scrollTop = y; }
5032 }
5033
5034 function scrollToRange(cm, range$$1) {
5035 resolveScrollToPos(cm);
5036 cm.curOp.scrollToPos = range$$1;
5037 }
5038
5039 // When an operation has its scrollToPos property set, and another
5040 // scroll action is applied before the end of the operation, this
5041 // 'simulates' scrolling that position into view in a cheap way, so
5042 // that the effect of intermediate scroll commands is not ignored.
5043 function resolveScrollToPos(cm) {
5044 var range$$1 = cm.curOp.scrollToPos;
5045 if (range$$1) {
5046 cm.curOp.scrollToPos = null;
5047 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
5048 scrollToCoordsRange(cm, from, to, range$$1.margin);
5049 }
5050 }
5051
5052 function scrollToCoordsRange(cm, from, to, margin) {
5053 var sPos = calculateScrollPos(cm, {
5054 left: Math.min(from.left, to.left),
5055 top: Math.min(from.top, to.top) - margin,
5056 right: Math.max(from.right, to.right),
5057 bottom: Math.max(from.bottom, to.bottom) + margin
5058 });
5059 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
5060 }
5061
5062 // Sync the scrollable area and scrollbars, ensure the viewport
5063 // covers the visible area.
5064 function updateScrollTop(cm, val) {
5065 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
5066 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
5067 setScrollTop(cm, val, true);
5068 if (gecko) { updateDisplaySimple(cm); }
5069 startWorker(cm, 100);
5070 }
5071
5072 function setScrollTop(cm, val, forceScroll) {
5073 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
5074 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
5075 cm.doc.scrollTop = val;
5076 cm.display.scrollbars.setScrollTop(val);
5077 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
5078 }
5079
5080 // Sync scroller and scrollbar, ensure the gutter elements are
5081 // aligned.
5082 function setScrollLeft(cm, val, isScroller, forceScroll) {
5083 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
5084 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
5085 cm.doc.scrollLeft = val;
5086 alignHorizontally(cm);
5087 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
5088 cm.display.scrollbars.setScrollLeft(val);
5089 }
5090
5091 // SCROLLBARS
5092
5093 // Prepare DOM reads needed to update the scrollbars. Done in one
5094 // shot to minimize update/measure roundtrips.
5095 function measureForScrollbars(cm) {
5096 var d = cm.display, gutterW = d.gutters.offsetWidth;
5097 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
5098 return {
5099 clientHeight: d.scroller.clientHeight,
5100 viewHeight: d.wrapper.clientHeight,
5101 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5102 viewWidth: d.wrapper.clientWidth,
5103 barLeft: cm.options.fixedGutter ? gutterW : 0,
5104 docHeight: docH,
5105 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5106 nativeBarWidth: d.nativeBarWidth,
5107 gutterWidth: gutterW
5108 }
5109 }
5110
5111 var NativeScrollbars = function(place, scroll, cm) {
5112 this.cm = cm;
5113 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5114 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5115 place(vert); place(horiz);
5116
5117 on(vert, "scroll", function () {
5118 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5119 });
5120 on(horiz, "scroll", function () {
5121 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5122 });
5123
5124 this.checkedZeroWidth = false;
5125 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5126 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5127 };
5128
5129 NativeScrollbars.prototype.update = function (measure) {
5130 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5131 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5132 var sWidth = measure.nativeBarWidth;
5133
5134 if (needsV) {
5135 this.vert.style.display = "block";
5136 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5137 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5138 // A bug in IE8 can cause this value to be negative, so guard it.
5139 this.vert.firstChild.style.height =
5140 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5141 } else {
5142 this.vert.style.display = "";
5143 this.vert.firstChild.style.height = "0";
5144 }
5145
5146 if (needsH) {
5147 this.horiz.style.display = "block";
5148 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5149 this.horiz.style.left = measure.barLeft + "px";
5150 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5151 this.horiz.firstChild.style.width =
5152 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5153 } else {
5154 this.horiz.style.display = "";
5155 this.horiz.firstChild.style.width = "0";
5156 }
5157
5158 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5159 if (sWidth == 0) { this.zeroWidthHack(); }
5160 this.checkedZeroWidth = true;
5161 }
5162
5163 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5164 };
5165
5166 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5167 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5168 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5169 };
5170
5171 NativeScrollbars.prototype.setScrollTop = function (pos) {
5172 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5173 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5174 };
5175
5176 NativeScrollbars.prototype.zeroWidthHack = function () {
5177 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5178 this.horiz.style.height = this.vert.style.width = w;
5179 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5180 this.disableHoriz = new Delayed;
5181 this.disableVert = new Delayed;
5182 };
5183
5184 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5185 bar.style.pointerEvents = "auto";
5186 function maybeDisable() {
5187 // To find out whether the scrollbar is still visible, we
5188 // check whether the element under the pixel in the bottom
5189 // right corner of the scrollbar box is the scrollbar box
5190 // itself (when the bar is still visible) or its filler child
5191 // (when the bar is hidden). If it is still visible, we keep
5192 // it enabled, if it's hidden, we disable pointer events.
5193 var box = bar.getBoundingClientRect();
5194 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5195 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5196 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5197 else { delay.set(1000, maybeDisable); }
5198 }
5199 delay.set(1000, maybeDisable);
5200 };
5201
5202 NativeScrollbars.prototype.clear = function () {
5203 var parent = this.horiz.parentNode;
5204 parent.removeChild(this.horiz);
5205 parent.removeChild(this.vert);
5206 };
5207
5208 var NullScrollbars = function () {};
5209
5210 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5211 NullScrollbars.prototype.setScrollLeft = function () {};
5212 NullScrollbars.prototype.setScrollTop = function () {};
5213 NullScrollbars.prototype.clear = function () {};
5214
5215 function updateScrollbars(cm, measure) {
5216 if (!measure) { measure = measureForScrollbars(cm); }
5217 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5218 updateScrollbarsInner(cm, measure);
5219 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5220 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5221 { updateHeightsInViewport(cm); }
5222 updateScrollbarsInner(cm, measureForScrollbars(cm));
5223 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5224 }
5225 }
5226
5227 // Re-synchronize the fake scrollbars with the actual size of the
5228 // content.
5229 function updateScrollbarsInner(cm, measure) {
5230 var d = cm.display;
5231 var sizes = d.scrollbars.update(measure);
5232
5233 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5234 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5235 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5236
5237 if (sizes.right && sizes.bottom) {
5238 d.scrollbarFiller.style.display = "block";
5239 d.scrollbarFiller.style.height = sizes.bottom + "px";
5240 d.scrollbarFiller.style.width = sizes.right + "px";
5241 } else { d.scrollbarFiller.style.display = ""; }
5242 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5243 d.gutterFiller.style.display = "block";
5244 d.gutterFiller.style.height = sizes.bottom + "px";
5245 d.gutterFiller.style.width = measure.gutterWidth + "px";
5246 } else { d.gutterFiller.style.display = ""; }
5247 }
5248
5249 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5250
5251 function initScrollbars(cm) {
5252 if (cm.display.scrollbars) {
5253 cm.display.scrollbars.clear();
5254 if (cm.display.scrollbars.addClass)
5255 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5256 }
5257
5258 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5259 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5260 // Prevent clicks in the scrollbars from killing focus
5261 on(node, "mousedown", function () {
5262 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5263 });
5264 node.setAttribute("cm-not-content", "true");
5265 }, function (pos, axis) {
5266 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5267 else { updateScrollTop(cm, pos); }
5268 }, cm);
5269 if (cm.display.scrollbars.addClass)
5270 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5271 }
5272
5273 // Operations are used to wrap a series of changes to the editor
5274 // state in such a way that each change won't have to update the
5275 // cursor and display (which would be awkward, slow, and
5276 // error-prone). Instead, display updates are batched and then all
5277 // combined and executed at once.
5278
5279 var nextOpId = 0;
5280 // Start a new operation.
5281 function startOperation(cm) {
5282 cm.curOp = {
5283 cm: cm,
5284 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5285 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5286 forceUpdate: false, // Used to force a redraw
5287 updateInput: null, // Whether to reset the input textarea
5288 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5289 changeObjs: null, // Accumulated changes, for firing change events
5290 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5291 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5292 selectionChanged: false, // Whether the selection needs to be redrawn
5293 updateMaxLine: false, // Set when the widest line needs to be determined anew
5294 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5295 scrollToPos: null, // Used to scroll to a specific position
5296 focus: false,
5297 id: ++nextOpId // Unique ID
5298 };
5299 pushOperation(cm.curOp);
5300 }
5301
5302 // Finish an operation, updating the display and signalling delayed events
5303 function endOperation(cm) {
5304 var op = cm.curOp;
5305 finishOperation(op, function (group) {
5306 for (var i = 0; i < group.ops.length; i++)
5307 { group.ops[i].cm.curOp = null; }
5308 endOperations(group);
5309 });
5310 }
5311
5312 // The DOM updates done when an operation finishes are batched so
5313 // that the minimum number of relayouts are required.
5314 function endOperations(group) {
5315 var ops = group.ops;
5316 for (var i = 0; i < ops.length; i++) // Read DOM
5317 { endOperation_R1(ops[i]); }
5318 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5319 { endOperation_W1(ops[i$1]); }
5320 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5321 { endOperation_R2(ops[i$2]); }
5322 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5323 { endOperation_W2(ops[i$3]); }
5324 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5325 { endOperation_finish(ops[i$4]); }
5326 }
5327
5328 function endOperation_R1(op) {
5329 var cm = op.cm, display = cm.display;
5330 maybeClipScrollbars(cm);
5331 if (op.updateMaxLine) { findMaxLine(cm); }
5332
5333 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5334 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5335 op.scrollToPos.to.line >= display.viewTo) ||
5336 display.maxLineChanged && cm.options.lineWrapping;
5337 op.update = op.mustUpdate &&
5338 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5339 }
5340
5341 function endOperation_W1(op) {
5342 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5343 }
5344
5345 function endOperation_R2(op) {
5346 var cm = op.cm, display = cm.display;
5347 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5348
5349 op.barMeasure = measureForScrollbars(cm);
5350
5351 // If the max line changed since it was last measured, measure it,
5352 // and ensure the document's width matches it.
5353 // updateDisplay_W2 will use these properties to do the actual resizing
5354 if (display.maxLineChanged && !cm.options.lineWrapping) {
5355 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5356 cm.display.sizerWidth = op.adjustWidthTo;
5357 op.barMeasure.scrollWidth =
5358 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5359 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5360 }
5361
5362 if (op.updatedDisplay || op.selectionChanged)
5363 { op.preparedSelection = display.input.prepareSelection(); }
5364 }
5365
5366 function endOperation_W2(op) {
5367 var cm = op.cm;
5368
5369 if (op.adjustWidthTo != null) {
5370 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5371 if (op.maxScrollLeft < cm.doc.scrollLeft)
5372 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5373 cm.display.maxLineChanged = false;
5374 }
5375
5376 var takeFocus = op.focus && op.focus == activeElt();
5377 if (op.preparedSelection)
5378 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5379 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5380 { updateScrollbars(cm, op.barMeasure); }
5381 if (op.updatedDisplay)
5382 { setDocumentHeight(cm, op.barMeasure); }
5383
5384 if (op.selectionChanged) { restartBlink(cm); }
5385
5386 if (cm.state.focused && op.updateInput)
5387 { cm.display.input.reset(op.typing); }
5388 if (takeFocus) { ensureFocus(op.cm); }
5389 }
5390
5391 function endOperation_finish(op) {
5392 var cm = op.cm, display = cm.display, doc = cm.doc;
5393
5394 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5395
5396 // Abort mouse wheel delta measurement, when scrolling explicitly
5397 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5398 { display.wheelStartX = display.wheelStartY = null; }
5399
5400 // Propagate the scroll position to the actual DOM scroller
5401 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5402
5403 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5404 // If we need to scroll a specific position into view, do so.
5405 if (op.scrollToPos) {
5406 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5407 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5408 maybeScrollWindow(cm, rect);
5409 }
5410
5411 // Fire events for markers that are hidden/unidden by editing or
5412 // undoing
5413 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5414 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5415 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5416 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5417 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5418
5419 if (display.wrapper.offsetHeight)
5420 { doc.scrollTop = cm.display.scroller.scrollTop; }
5421
5422 // Fire change events, and delayed event handlers
5423 if (op.changeObjs)
5424 { signal(cm, "changes", cm, op.changeObjs); }
5425 if (op.update)
5426 { op.update.finish(); }
5427 }
5428
5429 // Run the given function in an operation
5430 function runInOp(cm, f) {
5431 if (cm.curOp) { return f() }
5432 startOperation(cm);
5433 try { return f() }
5434 finally { endOperation(cm); }
5435 }
5436 // Wraps a function in an operation. Returns the wrapped function.
5437 function operation(cm, f) {
5438 return function() {
5439 if (cm.curOp) { return f.apply(cm, arguments) }
5440 startOperation(cm);
5441 try { return f.apply(cm, arguments) }
5442 finally { endOperation(cm); }
5443 }
5444 }
5445 // Used to add methods to editor and doc instances, wrapping them in
5446 // operations.
5447 function methodOp(f) {
5448 return function() {
5449 if (this.curOp) { return f.apply(this, arguments) }
5450 startOperation(this);
5451 try { return f.apply(this, arguments) }
5452 finally { endOperation(this); }
5453 }
5454 }
5455 function docMethodOp(f) {
5456 return function() {
5457 var cm = this.cm;
5458 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5459 startOperation(cm);
5460 try { return f.apply(this, arguments) }
5461 finally { endOperation(cm); }
5462 }
5463 }
5464
5465 // Updates the display.view data structure for a given change to the
5466 // document. From and to are in pre-change coordinates. Lendiff is
5467 // the amount of lines added or subtracted by the change. This is
5468 // used for changes that span multiple lines, or change the way
5469 // lines are divided into visual lines. regLineChange (below)
5470 // registers single-line changes.
5471 function regChange(cm, from, to, lendiff) {
5472 if (from == null) { from = cm.doc.first; }
5473 if (to == null) { to = cm.doc.first + cm.doc.size; }
5474 if (!lendiff) { lendiff = 0; }
5475
5476 var display = cm.display;
5477 if (lendiff && to < display.viewTo &&
5478 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5479 { display.updateLineNumbers = from; }
5480
5481 cm.curOp.viewChanged = true;
5482
5483 if (from >= display.viewTo) { // Change after
5484 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5485 { resetView(cm); }
5486 } else if (to <= display.viewFrom) { // Change before
5487 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5488 resetView(cm);
5489 } else {
5490 display.viewFrom += lendiff;
5491 display.viewTo += lendiff;
5492 }
5493 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5494 resetView(cm);
5495 } else if (from <= display.viewFrom) { // Top overlap
5496 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5497 if (cut) {
5498 display.view = display.view.slice(cut.index);
5499 display.viewFrom = cut.lineN;
5500 display.viewTo += lendiff;
5501 } else {
5502 resetView(cm);
5503 }
5504 } else if (to >= display.viewTo) { // Bottom overlap
5505 var cut$1 = viewCuttingPoint(cm, from, from, -1);
5506 if (cut$1) {
5507 display.view = display.view.slice(0, cut$1.index);
5508 display.viewTo = cut$1.lineN;
5509 } else {
5510 resetView(cm);
5511 }
5512 } else { // Gap in the middle
5513 var cutTop = viewCuttingPoint(cm, from, from, -1);
5514 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5515 if (cutTop && cutBot) {
5516 display.view = display.view.slice(0, cutTop.index)
5517 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5518 .concat(display.view.slice(cutBot.index));
5519 display.viewTo += lendiff;
5520 } else {
5521 resetView(cm);
5522 }
5523 }
5524
5525 var ext = display.externalMeasured;
5526 if (ext) {
5527 if (to < ext.lineN)
5528 { ext.lineN += lendiff; }
5529 else if (from < ext.lineN + ext.size)
5530 { display.externalMeasured = null; }
5531 }
5532 }
5533
5534 // Register a change to a single line. Type must be one of "text",
5535 // "gutter", "class", "widget"
5536 function regLineChange(cm, line, type) {
5537 cm.curOp.viewChanged = true;
5538 var display = cm.display, ext = cm.display.externalMeasured;
5539 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5540 { display.externalMeasured = null; }
5541
5542 if (line < display.viewFrom || line >= display.viewTo) { return }
5543 var lineView = display.view[findViewIndex(cm, line)];
5544 if (lineView.node == null) { return }
5545 var arr = lineView.changes || (lineView.changes = []);
5546 if (indexOf(arr, type) == -1) { arr.push(type); }
5547 }
5548
5549 // Clear the view.
5550 function resetView(cm) {
5551 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5552 cm.display.view = [];
5553 cm.display.viewOffset = 0;
5554 }
5555
5556 function viewCuttingPoint(cm, oldN, newN, dir) {
5557 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5558 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5559 { return {index: index, lineN: newN} }
5560 var n = cm.display.viewFrom;
5561 for (var i = 0; i < index; i++)
5562 { n += view[i].size; }
5563 if (n != oldN) {
5564 if (dir > 0) {
5565 if (index == view.length - 1) { return null }
5566 diff = (n + view[index].size) - oldN;
5567 index++;
5568 } else {
5569 diff = n - oldN;
5570 }
5571 oldN += diff; newN += diff;
5572 }
5573 while (visualLineNo(cm.doc, newN) != newN) {
5574 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
5575 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5576 index += dir;
5577 }
5578 return {index: index, lineN: newN}
5579 }
5580
5581 // Force the view to cover a given range, adding empty view element
5582 // or clipping off existing ones as needed.
5583 function adjustView(cm, from, to) {
5584 var display = cm.display, view = display.view;
5585 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5586 display.view = buildViewArray(cm, from, to);
5587 display.viewFrom = from;
5588 } else {
5589 if (display.viewFrom > from)
5590 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
5591 else if (display.viewFrom < from)
5592 { display.view = display.view.slice(findViewIndex(cm, from)); }
5593 display.viewFrom = from;
5594 if (display.viewTo < to)
5595 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
5596 else if (display.viewTo > to)
5597 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
5598 }
5599 display.viewTo = to;
5600 }
5601
5602 // Count the number of lines in the view whose DOM representation is
5603 // out of date (or nonexistent).
5604 function countDirtyView(cm) {
5605 var view = cm.display.view, dirty = 0;
5606 for (var i = 0; i < view.length; i++) {
5607 var lineView = view[i];
5608 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
5609 }
5610 return dirty
5611 }
5612
5613 // HIGHLIGHT WORKER
5614
5615 function startWorker(cm, time) {
5616 if (cm.doc.highlightFrontier < cm.display.viewTo)
5617 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5618 }
5619
5620 function highlightWorker(cm) {
5621 var doc = cm.doc;
5622 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5623 var end = +new Date + cm.options.workTime;
5624 var context = getContextBefore(cm, doc.highlightFrontier);
5625 var changedLines = [];
5626
5627 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5628 if (context.line >= cm.display.viewFrom) { // Visible
5629 var oldStyles = line.styles;
5630 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5631 var highlighted = highlightLine(cm, line, context, true);
5632 if (resetState) { context.state = resetState; }
5633 line.styles = highlighted.styles;
5634 var oldCls = line.styleClasses, newCls = highlighted.classes;
5635 if (newCls) { line.styleClasses = newCls; }
5636 else if (oldCls) { line.styleClasses = null; }
5637 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5638 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5639 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5640 if (ischange) { changedLines.push(context.line); }
5641 line.stateAfter = context.save();
5642 context.nextLine();
5643 } else {
5644 if (line.text.length <= cm.options.maxHighlightLength)
5645 { processLine(cm, line.text, context); }
5646 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5647 context.nextLine();
5648 }
5649 if (+new Date > end) {
5650 startWorker(cm, cm.options.workDelay);
5651 return true
5652 }
5653 });
5654 doc.highlightFrontier = context.line;
5655 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5656 if (changedLines.length) { runInOp(cm, function () {
5657 for (var i = 0; i < changedLines.length; i++)
5658 { regLineChange(cm, changedLines[i], "text"); }
5659 }); }
5660 }
5661
5662 // DISPLAY DRAWING
5663
5664 var DisplayUpdate = function(cm, viewport, force) {
5665 var display = cm.display;
5666
5667 this.viewport = viewport;
5668 // Store some values that we'll need later (but don't want to force a relayout for)
5669 this.visible = visibleLines(display, cm.doc, viewport);
5670 this.editorIsHidden = !display.wrapper.offsetWidth;
5671 this.wrapperHeight = display.wrapper.clientHeight;
5672 this.wrapperWidth = display.wrapper.clientWidth;
5673 this.oldDisplayWidth = displayWidth(cm);
5674 this.force = force;
5675 this.dims = getDimensions(cm);
5676 this.events = [];
5677 };
5678
5679 DisplayUpdate.prototype.signal = function (emitter, type) {
5680 if (hasHandler(emitter, type))
5681 { this.events.push(arguments); }
5682 };
5683 DisplayUpdate.prototype.finish = function () {
5684 var this$1 = this;
5685
5686 for (var i = 0; i < this.events.length; i++)
5687 { signal.apply(null, this$1.events[i]); }
5688 };
5689
5690 function maybeClipScrollbars(cm) {
5691 var display = cm.display;
5692 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5693 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5694 display.heightForcer.style.height = scrollGap(cm) + "px";
5695 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5696 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5697 display.scrollbarsClipped = true;
5698 }
5699 }
5700
5701 function selectionSnapshot(cm) {
5702 if (cm.hasFocus()) { return null }
5703 var active = activeElt();
5704 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5705 var result = {activeElt: active};
5706 if (window.getSelection) {
5707 var sel = window.getSelection();
5708 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5709 result.anchorNode = sel.anchorNode;
5710 result.anchorOffset = sel.anchorOffset;
5711 result.focusNode = sel.focusNode;
5712 result.focusOffset = sel.focusOffset;
5713 }
5714 }
5715 return result
5716 }
5717
5718 function restoreSelection(snapshot) {
5719 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5720 snapshot.activeElt.focus();
5721 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5722 var sel = window.getSelection(), range$$1 = document.createRange();
5723 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5724 range$$1.collapse(false);
5725 sel.removeAllRanges();
5726 sel.addRange(range$$1);
5727 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5728 }
5729 }
5730
5731 // Does the actual updating of the line display. Bails out
5732 // (returning false) when there is nothing to be done and forced is
5733 // false.
5734 function updateDisplayIfNeeded(cm, update) {
5735 var display = cm.display, doc = cm.doc;
5736
5737 if (update.editorIsHidden) {
5738 resetView(cm);
5739 return false
5740 }
5741
5742 // Bail out if the visible area is already rendered and nothing changed.
5743 if (!update.force &&
5744 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5745 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5746 display.renderedView == display.view && countDirtyView(cm) == 0)
5747 { return false }
5748
5749 if (maybeUpdateLineNumberWidth(cm)) {
5750 resetView(cm);
5751 update.dims = getDimensions(cm);
5752 }
5753
5754 // Compute a suitable new viewport (from & to)
5755 var end = doc.first + doc.size;
5756 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5757 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5758 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5759 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5760 if (sawCollapsedSpans) {
5761 from = visualLineNo(cm.doc, from);
5762 to = visualLineEndNo(cm.doc, to);
5763 }
5764
5765 var different = from != display.viewFrom || to != display.viewTo ||
5766 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5767 adjustView(cm, from, to);
5768
5769 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5770 // Position the mover div to align with the current scroll position
5771 cm.display.mover.style.top = display.viewOffset + "px";
5772
5773 var toUpdate = countDirtyView(cm);
5774 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5775 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5776 { return false }
5777
5778 // For big changes, we hide the enclosing element during the
5779 // update, since that speeds up the operations on most browsers.
5780 var selSnapshot = selectionSnapshot(cm);
5781 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5782 patchDisplay(cm, display.updateLineNumbers, update.dims);
5783 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5784 display.renderedView = display.view;
5785 // There might have been a widget with a focused element that got
5786 // hidden or updated, if so re-focus it.
5787 restoreSelection(selSnapshot);
5788
5789 // Prevent selection and cursors from interfering with the scroll
5790 // width and height.
5791 removeChildren(display.cursorDiv);
5792 removeChildren(display.selectionDiv);
5793 display.gutters.style.height = display.sizer.style.minHeight = 0;
5794
5795 if (different) {
5796 display.lastWrapHeight = update.wrapperHeight;
5797 display.lastWrapWidth = update.wrapperWidth;
5798 startWorker(cm, 400);
5799 }
5800
5801 display.updateLineNumbers = null;
5802
5803 return true
5804 }
5805
5806 function postUpdateDisplay(cm, update) {
5807 var viewport = update.viewport;
5808
5809 for (var first = true;; first = false) {
5810 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5811 // Clip forced viewport to actual scrollable area.
5812 if (viewport && viewport.top != null)
5813 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5814 // Updated line heights might result in the drawn area not
5815 // actually covering the viewport. Keep looping until it does.
5816 update.visible = visibleLines(cm.display, cm.doc, viewport);
5817 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5818 { break }
5819 }
5820 if (!updateDisplayIfNeeded(cm, update)) { break }
5821 updateHeightsInViewport(cm);
5822 var barMeasure = measureForScrollbars(cm);
5823 updateSelection(cm);
5824 updateScrollbars(cm, barMeasure);
5825 setDocumentHeight(cm, barMeasure);
5826 update.force = false;
5827 }
5828
5829 update.signal(cm, "update", cm);
5830 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5831 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5832 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5833 }
5834 }
5835
5836 function updateDisplaySimple(cm, viewport) {
5837 var update = new DisplayUpdate(cm, viewport);
5838 if (updateDisplayIfNeeded(cm, update)) {
5839 updateHeightsInViewport(cm);
5840 postUpdateDisplay(cm, update);
5841 var barMeasure = measureForScrollbars(cm);
5842 updateSelection(cm);
5843 updateScrollbars(cm, barMeasure);
5844 setDocumentHeight(cm, barMeasure);
5845 update.finish();
5846 }
5847 }
5848
5849 // Sync the actual display DOM structure with display.view, removing
5850 // nodes for lines that are no longer in view, and creating the ones
5851 // that are not there yet, and updating the ones that are out of
5852 // date.
5853 function patchDisplay(cm, updateNumbersFrom, dims) {
5854 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5855 var container = display.lineDiv, cur = container.firstChild;
5856
5857 function rm(node) {
5858 var next = node.nextSibling;
5859 // Works around a throw-scroll bug in OS X Webkit
5860 if (webkit && mac && cm.display.currentWheelTarget == node)
5861 { node.style.display = "none"; }
5862 else
5863 { node.parentNode.removeChild(node); }
5864 return next
5865 }
5866
5867 var view = display.view, lineN = display.viewFrom;
5868 // Loop over the elements in the view, syncing cur (the DOM nodes
5869 // in display.lineDiv) with the view as we go.
5870 for (var i = 0; i < view.length; i++) {
5871 var lineView = view[i];
5872 if (lineView.hidden) {
5873 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5874 var node = buildLineElement(cm, lineView, lineN, dims);
5875 container.insertBefore(node, cur);
5876 } else { // Already drawn
5877 while (cur != lineView.node) { cur = rm(cur); }
5878 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5879 updateNumbersFrom <= lineN && lineView.lineNumber;
5880 if (lineView.changes) {
5881 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5882 updateLineForChanges(cm, lineView, lineN, dims);
5883 }
5884 if (updateNumber) {
5885 removeChildren(lineView.lineNumber);
5886 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5887 }
5888 cur = lineView.node.nextSibling;
5889 }
5890 lineN += lineView.size;
5891 }
5892 while (cur) { cur = rm(cur); }
5893 }
5894
5895 function updateGutterSpace(cm) {
5896 var width = cm.display.gutters.offsetWidth;
5897 cm.display.sizer.style.marginLeft = width + "px";
5898 }
5899
5900 function setDocumentHeight(cm, measure) {
5901 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5902 cm.display.heightForcer.style.top = measure.docHeight + "px";
5903 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5904 }
5905
5906 // Rebuild the gutter elements, ensure the margin to the left of the
5907 // code matches their width.
5908 function updateGutters(cm) {
5909 var gutters = cm.display.gutters, specs = cm.options.gutters;
5910 removeChildren(gutters);
5911 var i = 0;
5912 for (; i < specs.length; ++i) {
5913 var gutterClass = specs[i];
5914 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
5915 if (gutterClass == "CodeMirror-linenumbers") {
5916 cm.display.lineGutter = gElt;
5917 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
5918 }
5919 }
5920 gutters.style.display = i ? "" : "none";
5921 updateGutterSpace(cm);
5922 }
5923
5924 // Make sure the gutters options contains the element
5925 // "CodeMirror-linenumbers" when the lineNumbers option is true.
5926 function setGuttersForLineNumbers(options) {
5927 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
5928 if (found == -1 && options.lineNumbers) {
5929 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
5930 } else if (found > -1 && !options.lineNumbers) {
5931 options.gutters = options.gutters.slice(0);
5932 options.gutters.splice(found, 1);
5933 }
5934 }
5935
5936 // Since the delta values reported on mouse wheel events are
5937 // unstandardized between browsers and even browser versions, and
5938 // generally horribly unpredictable, this code starts by measuring
5939 // the scroll effect that the first few mouse wheel events have,
5940 // and, from that, detects the way it can convert deltas to pixel
5941 // offsets afterwards.
5942 //
5943 // The reason we want to know the amount a wheel event will scroll
5944 // is that it gives us a chance to update the display before the
5945 // actual scrolling happens, reducing flickering.
5946
5947 var wheelSamples = 0;
5948 var wheelPixelsPerUnit = null;
5949 // Fill in a browser-detected starting value on browsers where we
5950 // know one. These don't have to be accurate -- the result of them
5951 // being wrong would just be a slight flicker on the first wheel
5952 // scroll (if it is large enough).
5953 if (ie) { wheelPixelsPerUnit = -.53; }
5954 else if (gecko) { wheelPixelsPerUnit = 15; }
5955 else if (chrome) { wheelPixelsPerUnit = -.7; }
5956 else if (safari) { wheelPixelsPerUnit = -1/3; }
5957
5958 function wheelEventDelta(e) {
5959 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
5960 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
5961 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
5962 else if (dy == null) { dy = e.wheelDelta; }
5963 return {x: dx, y: dy}
5964 }
5965 function wheelEventPixels(e) {
5966 var delta = wheelEventDelta(e);
5967 delta.x *= wheelPixelsPerUnit;
5968 delta.y *= wheelPixelsPerUnit;
5969 return delta
5970 }
5971
5972 function onScrollWheel(cm, e) {
5973 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
5974
5975 var display = cm.display, scroll = display.scroller;
5976 // Quit if there's nothing to scroll here
5977 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
5978 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
5979 if (!(dx && canScrollX || dy && canScrollY)) { return }
5980
5981 // Webkit browsers on OS X abort momentum scrolls when the target
5982 // of the scroll event is removed from the scrollable element.
5983 // This hack (see related code in patchDisplay) makes sure the
5984 // element is kept around.
5985 if (dy && mac && webkit) {
5986 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
5987 for (var i = 0; i < view.length; i++) {
5988 if (view[i].node == cur) {
5989 cm.display.currentWheelTarget = cur;
5990 break outer
5991 }
5992 }
5993 }
5994 }
5995
5996 // On some browsers, horizontal scrolling will cause redraws to
5997 // happen before the gutter has been realigned, causing it to
5998 // wriggle around in a most unseemly way. When we have an
5999 // estimated pixels/delta value, we just handle horizontal
6000 // scrolling entirely here. It'll be slightly off from native, but
6001 // better than glitching out.
6002 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6003 if (dy && canScrollY)
6004 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
6005 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
6006 // Only prevent default scrolling if vertical scrolling is
6007 // actually possible. Otherwise, it causes vertical scroll
6008 // jitter on OSX trackpads when deltaX is small and deltaY
6009 // is large (issue #3579)
6010 if (!dy || (dy && canScrollY))
6011 { e_preventDefault(e); }
6012 display.wheelStartX = null; // Abort measurement, if in progress
6013 return
6014 }
6015
6016 // 'Project' the visible viewport to cover the area that is being
6017 // scrolled into view (if we know enough to estimate it).
6018 if (dy && wheelPixelsPerUnit != null) {
6019 var pixels = dy * wheelPixelsPerUnit;
6020 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6021 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
6022 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
6023 updateDisplaySimple(cm, {top: top, bottom: bot});
6024 }
6025
6026 if (wheelSamples < 20) {
6027 if (display.wheelStartX == null) {
6028 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6029 display.wheelDX = dx; display.wheelDY = dy;
6030 setTimeout(function () {
6031 if (display.wheelStartX == null) { return }
6032 var movedX = scroll.scrollLeft - display.wheelStartX;
6033 var movedY = scroll.scrollTop - display.wheelStartY;
6034 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6035 (movedX && display.wheelDX && movedX / display.wheelDX);
6036 display.wheelStartX = display.wheelStartY = null;
6037 if (!sample) { return }
6038 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6039 ++wheelSamples;
6040 }, 200);
6041 } else {
6042 display.wheelDX += dx; display.wheelDY += dy;
6043 }
6044 }
6045 }
6046
6047 // Selection objects are immutable. A new one is created every time
6048 // the selection changes. A selection is one or more non-overlapping
6049 // (and non-touching) ranges, sorted, and an integer that indicates
6050 // which one is the primary selection (the one that's scrolled into
6051 // view, that getCursor returns, etc).
6052 var Selection = function(ranges, primIndex) {
6053 this.ranges = ranges;
6054 this.primIndex = primIndex;
6055 };
6056
6057 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
6058
6059 Selection.prototype.equals = function (other) {
6060 var this$1 = this;
6061
6062 if (other == this) { return true }
6063 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
6064 for (var i = 0; i < this.ranges.length; i++) {
6065 var here = this$1.ranges[i], there = other.ranges[i];
6066 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
6067 }
6068 return true
6069 };
6070
6071 Selection.prototype.deepCopy = function () {
6072 var this$1 = this;
6073
6074 var out = [];
6075 for (var i = 0; i < this.ranges.length; i++)
6076 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
6077 return new Selection(out, this.primIndex)
6078 };
6079
6080 Selection.prototype.somethingSelected = function () {
6081 var this$1 = this;
6082
6083 for (var i = 0; i < this.ranges.length; i++)
6084 { if (!this$1.ranges[i].empty()) { return true } }
6085 return false
6086 };
6087
6088 Selection.prototype.contains = function (pos, end) {
6089 var this$1 = this;
6090
6091 if (!end) { end = pos; }
6092 for (var i = 0; i < this.ranges.length; i++) {
6093 var range = this$1.ranges[i];
6094 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
6095 { return i }
6096 }
6097 return -1
6098 };
6099
6100 var Range = function(anchor, head) {
6101 this.anchor = anchor; this.head = head;
6102 };
6103
6104 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6105 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6106 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6107
6108 // Take an unsorted, potentially overlapping set of ranges, and
6109 // build a selection out of it. 'Consumes' ranges array (modifying
6110 // it).
6111 function normalizeSelection(ranges, primIndex) {
6112 var prim = ranges[primIndex];
6113 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6114 primIndex = indexOf(ranges, prim);
6115 for (var i = 1; i < ranges.length; i++) {
6116 var cur = ranges[i], prev = ranges[i - 1];
6117 if (cmp(prev.to(), cur.from()) >= 0) {
6118 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6119 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6120 if (i <= primIndex) { --primIndex; }
6121 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6122 }
6123 }
6124 return new Selection(ranges, primIndex)
6125 }
6126
6127 function simpleSelection(anchor, head) {
6128 return new Selection([new Range(anchor, head || anchor)], 0)
6129 }
6130
6131 // Compute the position of the end of a change (its 'to' property
6132 // refers to the pre-change end).
6133 function changeEnd(change) {
6134 if (!change.text) { return change.to }
6135 return Pos(change.from.line + change.text.length - 1,
6136 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6137 }
6138
6139 // Adjust a position to refer to the post-change position of the
6140 // same text, or the end of the change if the change covers it.
6141 function adjustForChange(pos, change) {
6142 if (cmp(pos, change.from) < 0) { return pos }
6143 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6144
6145 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6146 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6147 return Pos(line, ch)
6148 }
6149
6150 function computeSelAfterChange(doc, change) {
6151 var out = [];
6152 for (var i = 0; i < doc.sel.ranges.length; i++) {
6153 var range = doc.sel.ranges[i];
6154 out.push(new Range(adjustForChange(range.anchor, change),
6155 adjustForChange(range.head, change)));
6156 }
6157 return normalizeSelection(out, doc.sel.primIndex)
6158 }
6159
6160 function offsetPos(pos, old, nw) {
6161 if (pos.line == old.line)
6162 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6163 else
6164 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6165 }
6166
6167 // Used by replaceSelections to allow moving the selection to the
6168 // start or around the replaced test. Hint may be "start" or "around".
6169 function computeReplacedSel(doc, changes, hint) {
6170 var out = [];
6171 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6172 for (var i = 0; i < changes.length; i++) {
6173 var change = changes[i];
6174 var from = offsetPos(change.from, oldPrev, newPrev);
6175 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6176 oldPrev = change.to;
6177 newPrev = to;
6178 if (hint == "around") {
6179 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6180 out[i] = new Range(inv ? to : from, inv ? from : to);
6181 } else {
6182 out[i] = new Range(from, from);
6183 }
6184 }
6185 return new Selection(out, doc.sel.primIndex)
6186 }
6187
6188 // Used to get the editor into a consistent state again when options change.
6189
6190 function loadMode(cm) {
6191 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6192 resetModeState(cm);
6193 }
6194
6195 function resetModeState(cm) {
6196 cm.doc.iter(function (line) {
6197 if (line.stateAfter) { line.stateAfter = null; }
6198 if (line.styles) { line.styles = null; }
6199 });
6200 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6201 startWorker(cm, 100);
6202 cm.state.modeGen++;
6203 if (cm.curOp) { regChange(cm); }
6204 }
6205
6206 // DOCUMENT DATA STRUCTURE
6207
6208 // By default, updates that start and end at the beginning of a line
6209 // are treated specially, in order to make the association of line
6210 // widgets and marker elements with the text behave more intuitive.
6211 function isWholeLineUpdate(doc, change) {
6212 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6213 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6214 }
6215
6216 // Perform a change on the document data structure.
6217 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6218 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6219 function update(line, text, spans) {
6220 updateLine(line, text, spans, estimateHeight$$1);
6221 signalLater(line, "change", line, change);
6222 }
6223 function linesFor(start, end) {
6224 var result = [];
6225 for (var i = start; i < end; ++i)
6226 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6227 return result
6228 }
6229
6230 var from = change.from, to = change.to, text = change.text;
6231 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6232 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6233
6234 // Adjust the line structure
6235 if (change.full) {
6236 doc.insert(0, linesFor(0, text.length));
6237 doc.remove(text.length, doc.size - text.length);
6238 } else if (isWholeLineUpdate(doc, change)) {
6239 // This is a whole-line replace. Treated specially to make
6240 // sure line objects move the way they are supposed to.
6241 var added = linesFor(0, text.length - 1);
6242 update(lastLine, lastLine.text, lastSpans);
6243 if (nlines) { doc.remove(from.line, nlines); }
6244 if (added.length) { doc.insert(from.line, added); }
6245 } else if (firstLine == lastLine) {
6246 if (text.length == 1) {
6247 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6248 } else {
6249 var added$1 = linesFor(1, text.length - 1);
6250 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6251 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6252 doc.insert(from.line + 1, added$1);
6253 }
6254 } else if (text.length == 1) {
6255 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6256 doc.remove(from.line + 1, nlines);
6257 } else {
6258 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6259 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6260 var added$2 = linesFor(1, text.length - 1);
6261 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6262 doc.insert(from.line + 1, added$2);
6263 }
6264
6265 signalLater(doc, "change", doc, change);
6266 }
6267
6268 // Call f for all linked documents.
6269 function linkedDocs(doc, f, sharedHistOnly) {
6270 function propagate(doc, skip, sharedHist) {
6271 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6272 var rel = doc.linked[i];
6273 if (rel.doc == skip) { continue }
6274 var shared = sharedHist && rel.sharedHist;
6275 if (sharedHistOnly && !shared) { continue }
6276 f(rel.doc, shared);
6277 propagate(rel.doc, doc, shared);
6278 } }
6279 }
6280 propagate(doc, null, true);
6281 }
6282
6283 // Attach a document to an editor.
6284 function attachDoc(cm, doc) {
6285 if (doc.cm) { throw new Error("This document is already in use.") }
6286 cm.doc = doc;
6287 doc.cm = cm;
6288 estimateLineHeights(cm);
6289 loadMode(cm);
6290 setDirectionClass(cm);
6291 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6292 cm.options.mode = doc.modeOption;
6293 regChange(cm);
6294 }
6295
6296 function setDirectionClass(cm) {
6297 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6298 }
6299
6300 function directionChanged(cm) {
6301 runInOp(cm, function () {
6302 setDirectionClass(cm);
6303 regChange(cm);
6304 });
6305 }
6306
6307 function History(startGen) {
6308 // Arrays of change events and selections. Doing something adds an
6309 // event to done and clears undo. Undoing moves events from done
6310 // to undone, redoing moves them in the other direction.
6311 this.done = []; this.undone = [];
6312 this.undoDepth = Infinity;
6313 // Used to track when changes can be merged into a single undo
6314 // event
6315 this.lastModTime = this.lastSelTime = 0;
6316 this.lastOp = this.lastSelOp = null;
6317 this.lastOrigin = this.lastSelOrigin = null;
6318 // Used by the isClean() method
6319 this.generation = this.maxGeneration = startGen || 1;
6320 }
6321
6322 // Create a history change event from an updateDoc-style change
6323 // object.
6324 function historyChangeFromChange(doc, change) {
6325 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6326 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6327 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6328 return histChange
6329 }
6330
6331 // Pop all selection events off the end of a history array. Stop at
6332 // a change event.
6333 function clearSelectionEvents(array) {
6334 while (array.length) {
6335 var last = lst(array);
6336 if (last.ranges) { array.pop(); }
6337 else { break }
6338 }
6339 }
6340
6341 // Find the top change event in the history. Pop off selection
6342 // events that are in the way.
6343 function lastChangeEvent(hist, force) {
6344 if (force) {
6345 clearSelectionEvents(hist.done);
6346 return lst(hist.done)
6347 } else if (hist.done.length && !lst(hist.done).ranges) {
6348 return lst(hist.done)
6349 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6350 hist.done.pop();
6351 return lst(hist.done)
6352 }
6353 }
6354
6355 // Register a change in the history. Merges changes that are within
6356 // a single operation, or are close together with an origin that
6357 // allows merging (starting with "+") into a single event.
6358 function addChangeToHistory(doc, change, selAfter, opId) {
6359 var hist = doc.history;
6360 hist.undone.length = 0;
6361 var time = +new Date, cur;
6362 var last;
6363
6364 if ((hist.lastOp == opId ||
6365 hist.lastOrigin == change.origin && change.origin &&
6366 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
6367 change.origin.charAt(0) == "*")) &&
6368 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6369 // Merge this change into the last event
6370 last = lst(cur.changes);
6371 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6372 // Optimized case for simple insertion -- don't want to add
6373 // new changesets for every character typed
6374 last.to = changeEnd(change);
6375 } else {
6376 // Add new sub-event
6377 cur.changes.push(historyChangeFromChange(doc, change));
6378 }
6379 } else {
6380 // Can not be merged, start a new event.
6381 var before = lst(hist.done);
6382 if (!before || !before.ranges)
6383 { pushSelectionToHistory(doc.sel, hist.done); }
6384 cur = {changes: [historyChangeFromChange(doc, change)],
6385 generation: hist.generation};
6386 hist.done.push(cur);
6387 while (hist.done.length > hist.undoDepth) {
6388 hist.done.shift();
6389 if (!hist.done[0].ranges) { hist.done.shift(); }
6390 }
6391 }
6392 hist.done.push(selAfter);
6393 hist.generation = ++hist.maxGeneration;
6394 hist.lastModTime = hist.lastSelTime = time;
6395 hist.lastOp = hist.lastSelOp = opId;
6396 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6397
6398 if (!last) { signal(doc, "historyAdded"); }
6399 }
6400
6401 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6402 var ch = origin.charAt(0);
6403 return ch == "*" ||
6404 ch == "+" &&
6405 prev.ranges.length == sel.ranges.length &&
6406 prev.somethingSelected() == sel.somethingSelected() &&
6407 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6408 }
6409
6410 // Called whenever the selection changes, sets the new selection as
6411 // the pending selection in the history, and pushes the old pending
6412 // selection into the 'done' array when it was significantly
6413 // different (in number of selected ranges, emptiness, or time).
6414 function addSelectionToHistory(doc, sel, opId, options) {
6415 var hist = doc.history, origin = options && options.origin;
6416
6417 // A new event is started when the previous origin does not match
6418 // the current, or the origins don't allow matching. Origins
6419 // starting with * are always merged, those starting with + are
6420 // merged when similar and close together in time.
6421 if (opId == hist.lastSelOp ||
6422 (origin && hist.lastSelOrigin == origin &&
6423 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6424 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6425 { hist.done[hist.done.length - 1] = sel; }
6426 else
6427 { pushSelectionToHistory(sel, hist.done); }
6428
6429 hist.lastSelTime = +new Date;
6430 hist.lastSelOrigin = origin;
6431 hist.lastSelOp = opId;
6432 if (options && options.clearRedo !== false)
6433 { clearSelectionEvents(hist.undone); }
6434 }
6435
6436 function pushSelectionToHistory(sel, dest) {
6437 var top = lst(dest);
6438 if (!(top && top.ranges && top.equals(sel)))
6439 { dest.push(sel); }
6440 }
6441
6442 // Used to store marked span information in the history.
6443 function attachLocalSpans(doc, change, from, to) {
6444 var existing = change["spans_" + doc.id], n = 0;
6445 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6446 if (line.markedSpans)
6447 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6448 ++n;
6449 });
6450 }
6451
6452 // When un/re-doing restores text containing marked spans, those
6453 // that have been explicitly cleared should not be restored.
6454 function removeClearedSpans(spans) {
6455 if (!spans) { return null }
6456 var out;
6457 for (var i = 0; i < spans.length; ++i) {
6458 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6459 else if (out) { out.push(spans[i]); }
6460 }
6461 return !out ? spans : out.length ? out : null
6462 }
6463
6464 // Retrieve and filter the old marked spans stored in a change event.
6465 function getOldSpans(doc, change) {
6466 var found = change["spans_" + doc.id];
6467 if (!found) { return null }
6468 var nw = [];
6469 for (var i = 0; i < change.text.length; ++i)
6470 { nw.push(removeClearedSpans(found[i])); }
6471 return nw
6472 }
6473
6474 // Used for un/re-doing changes from the history. Combines the
6475 // result of computing the existing spans with the set of spans that
6476 // existed in the history (so that deleting around a span and then
6477 // undoing brings back the span).
6478 function mergeOldSpans(doc, change) {
6479 var old = getOldSpans(doc, change);
6480 var stretched = stretchSpansOverChange(doc, change);
6481 if (!old) { return stretched }
6482 if (!stretched) { return old }
6483
6484 for (var i = 0; i < old.length; ++i) {
6485 var oldCur = old[i], stretchCur = stretched[i];
6486 if (oldCur && stretchCur) {
6487 spans: for (var j = 0; j < stretchCur.length; ++j) {
6488 var span = stretchCur[j];
6489 for (var k = 0; k < oldCur.length; ++k)
6490 { if (oldCur[k].marker == span.marker) { continue spans } }
6491 oldCur.push(span);
6492 }
6493 } else if (stretchCur) {
6494 old[i] = stretchCur;
6495 }
6496 }
6497 return old
6498 }
6499
6500 // Used both to provide a JSON-safe object in .getHistory, and, when
6501 // detaching a document, to split the history in two
6502 function copyHistoryArray(events, newGroup, instantiateSel) {
6503 var copy = [];
6504 for (var i = 0; i < events.length; ++i) {
6505 var event = events[i];
6506 if (event.ranges) {
6507 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6508 continue
6509 }
6510 var changes = event.changes, newChanges = [];
6511 copy.push({changes: newChanges});
6512 for (var j = 0; j < changes.length; ++j) {
6513 var change = changes[j], m = (void 0);
6514 newChanges.push({from: change.from, to: change.to, text: change.text});
6515 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6516 if (indexOf(newGroup, Number(m[1])) > -1) {
6517 lst(newChanges)[prop] = change[prop];
6518 delete change[prop];
6519 }
6520 } } }
6521 }
6522 }
6523 return copy
6524 }
6525
6526 // The 'scroll' parameter given to many of these indicated whether
6527 // the new cursor position should be scrolled into view after
6528 // modifying the selection.
6529
6530 // If shift is held or the extend flag is set, extends a range to
6531 // include a given position (and optionally a second position).
6532 // Otherwise, simply returns the range between the given positions.
6533 // Used for cursor motion and such.
6534 function extendRange(range, head, other, extend) {
6535 if (extend) {
6536 var anchor = range.anchor;
6537 if (other) {
6538 var posBefore = cmp(head, anchor) < 0;
6539 if (posBefore != (cmp(other, anchor) < 0)) {
6540 anchor = head;
6541 head = other;
6542 } else if (posBefore != (cmp(head, other) < 0)) {
6543 head = other;
6544 }
6545 }
6546 return new Range(anchor, head)
6547 } else {
6548 return new Range(other || head, head)
6549 }
6550 }
6551
6552 // Extend the primary selection range, discard the rest.
6553 function extendSelection(doc, head, other, options, extend) {
6554 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6555 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6556 }
6557
6558 // Extend all selections (pos is an array of selections with length
6559 // equal the number of selections)
6560 function extendSelections(doc, heads, options) {
6561 var out = [];
6562 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6563 for (var i = 0; i < doc.sel.ranges.length; i++)
6564 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6565 var newSel = normalizeSelection(out, doc.sel.primIndex);
6566 setSelection(doc, newSel, options);
6567 }
6568
6569 // Updates a single range in the selection.
6570 function replaceOneSelection(doc, i, range, options) {
6571 var ranges = doc.sel.ranges.slice(0);
6572 ranges[i] = range;
6573 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
6574 }
6575
6576 // Reset the selection to a single range.
6577 function setSimpleSelection(doc, anchor, head, options) {
6578 setSelection(doc, simpleSelection(anchor, head), options);
6579 }
6580
6581 // Give beforeSelectionChange handlers a change to influence a
6582 // selection update.
6583 function filterSelectionChange(doc, sel, options) {
6584 var obj = {
6585 ranges: sel.ranges,
6586 update: function(ranges) {
6587 var this$1 = this;
6588
6589 this.ranges = [];
6590 for (var i = 0; i < ranges.length; i++)
6591 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6592 clipPos(doc, ranges[i].head)); }
6593 },
6594 origin: options && options.origin
6595 };
6596 signal(doc, "beforeSelectionChange", doc, obj);
6597 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6598 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
6599 else { return sel }
6600 }
6601
6602 function setSelectionReplaceHistory(doc, sel, options) {
6603 var done = doc.history.done, last = lst(done);
6604 if (last && last.ranges) {
6605 done[done.length - 1] = sel;
6606 setSelectionNoUndo(doc, sel, options);
6607 } else {
6608 setSelection(doc, sel, options);
6609 }
6610 }
6611
6612 // Set a new selection.
6613 function setSelection(doc, sel, options) {
6614 setSelectionNoUndo(doc, sel, options);
6615 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6616 }
6617
6618 function setSelectionNoUndo(doc, sel, options) {
6619 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6620 { sel = filterSelectionChange(doc, sel, options); }
6621
6622 var bias = options && options.bias ||
6623 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6624 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6625
6626 if (!(options && options.scroll === false) && doc.cm)
6627 { ensureCursorVisible(doc.cm); }
6628 }
6629
6630 function setSelectionInner(doc, sel) {
6631 if (sel.equals(doc.sel)) { return }
6632
6633 doc.sel = sel;
6634
6635 if (doc.cm) {
6636 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
6637 signalCursorActivity(doc.cm);
6638 }
6639 signalLater(doc, "cursorActivity", doc);
6640 }
6641
6642 // Verify that the selection does not partially select any atomic
6643 // marked ranges.
6644 function reCheckSelection(doc) {
6645 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6646 }
6647
6648 // Return a selection that does not partially select any atomic
6649 // ranges.
6650 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6651 var out;
6652 for (var i = 0; i < sel.ranges.length; i++) {
6653 var range = sel.ranges[i];
6654 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6655 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6656 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6657 if (out || newAnchor != range.anchor || newHead != range.head) {
6658 if (!out) { out = sel.ranges.slice(0, i); }
6659 out[i] = new Range(newAnchor, newHead);
6660 }
6661 }
6662 return out ? normalizeSelection(out, sel.primIndex) : sel
6663 }
6664
6665 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6666 var line = getLine(doc, pos.line);
6667 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6668 var sp = line.markedSpans[i], m = sp.marker;
6669 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6670 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6671 if (mayClear) {
6672 signal(m, "beforeCursorEnter");
6673 if (m.explicitlyCleared) {
6674 if (!line.markedSpans) { break }
6675 else {--i; continue}
6676 }
6677 }
6678 if (!m.atomic) { continue }
6679
6680 if (oldPos) {
6681 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6682 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6683 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6684 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6685 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6686 }
6687
6688 var far = m.find(dir < 0 ? -1 : 1);
6689 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6690 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6691 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6692 }
6693 } }
6694 return pos
6695 }
6696
6697 // Ensure a given position is not inside an atomic range.
6698 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6699 var dir = bias || 1;
6700 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6701 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6702 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6703 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6704 if (!found) {
6705 doc.cantEdit = true;
6706 return Pos(doc.first, 0)
6707 }
6708 return found
6709 }
6710
6711 function movePos(doc, pos, dir, line) {
6712 if (dir < 0 && pos.ch == 0) {
6713 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6714 else { return null }
6715 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6716 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6717 else { return null }
6718 } else {
6719 return new Pos(pos.line, pos.ch + dir)
6720 }
6721 }
6722
6723 function selectAll(cm) {
6724 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6725 }
6726
6727 // UPDATING
6728
6729 // Allow "beforeChange" event handlers to influence a change
6730 function filterChange(doc, change, update) {
6731 var obj = {
6732 canceled: false,
6733 from: change.from,
6734 to: change.to,
6735 text: change.text,
6736 origin: change.origin,
6737 cancel: function () { return obj.canceled = true; }
6738 };
6739 if (update) { obj.update = function (from, to, text, origin) {
6740 if (from) { obj.from = clipPos(doc, from); }
6741 if (to) { obj.to = clipPos(doc, to); }
6742 if (text) { obj.text = text; }
6743 if (origin !== undefined) { obj.origin = origin; }
6744 }; }
6745 signal(doc, "beforeChange", doc, obj);
6746 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6747
6748 if (obj.canceled) { return null }
6749 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6750 }
6751
6752 // Apply a change to a document, and add it to the document's
6753 // history, and propagating it to all linked documents.
6754 function makeChange(doc, change, ignoreReadOnly) {
6755 if (doc.cm) {
6756 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6757 if (doc.cm.state.suppressEdits) { return }
6758 }
6759
6760 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6761 change = filterChange(doc, change, true);
6762 if (!change) { return }
6763 }
6764
6765 // Possibly split or suppress the update based on the presence
6766 // of read-only spans in its range.
6767 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6768 if (split) {
6769 for (var i = split.length - 1; i >= 0; --i)
6770 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6771 } else {
6772 makeChangeInner(doc, change);
6773 }
6774 }
6775
6776 function makeChangeInner(doc, change) {
6777 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6778 var selAfter = computeSelAfterChange(doc, change);
6779 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6780
6781 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6782 var rebased = [];
6783
6784 linkedDocs(doc, function (doc, sharedHist) {
6785 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6786 rebaseHist(doc.history, change);
6787 rebased.push(doc.history);
6788 }
6789 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6790 });
6791 }
6792
6793 // Revert a change stored in a document's history.
6794 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6795 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
6796
6797 var hist = doc.history, event, selAfter = doc.sel;
6798 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6799
6800 // Verify that there is a useable event (so that ctrl-z won't
6801 // needlessly clear selection events)
6802 var i = 0;
6803 for (; i < source.length; i++) {
6804 event = source[i];
6805 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6806 { break }
6807 }
6808 if (i == source.length) { return }
6809 hist.lastOrigin = hist.lastSelOrigin = null;
6810
6811 for (;;) {
6812 event = source.pop();
6813 if (event.ranges) {
6814 pushSelectionToHistory(event, dest);
6815 if (allowSelectionOnly && !event.equals(doc.sel)) {
6816 setSelection(doc, event, {clearRedo: false});
6817 return
6818 }
6819 selAfter = event;
6820 }
6821 else { break }
6822 }
6823
6824 // Build up a reverse change object to add to the opposite history
6825 // stack (redo when undoing, and vice versa).
6826 var antiChanges = [];
6827 pushSelectionToHistory(selAfter, dest);
6828 dest.push({changes: antiChanges, generation: hist.generation});
6829 hist.generation = event.generation || ++hist.maxGeneration;
6830
6831 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6832
6833 var loop = function ( i ) {
6834 var change = event.changes[i];
6835 change.origin = type;
6836 if (filter && !filterChange(doc, change, false)) {
6837 source.length = 0;
6838 return {}
6839 }
6840
6841 antiChanges.push(historyChangeFromChange(doc, change));
6842
6843 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6844 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6845 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6846 var rebased = [];
6847
6848 // Propagate to the linked documents
6849 linkedDocs(doc, function (doc, sharedHist) {
6850 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6851 rebaseHist(doc.history, change);
6852 rebased.push(doc.history);
6853 }
6854 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6855 });
6856 };
6857
6858 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6859 var returned = loop( i$1 );
6860
6861 if ( returned ) return returned.v;
6862 }
6863 }
6864
6865 // Sub-views need their line numbers shifted when text is added
6866 // above or below them in the parent document.
6867 function shiftDoc(doc, distance) {
6868 if (distance == 0) { return }
6869 doc.first += distance;
6870 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6871 Pos(range.anchor.line + distance, range.anchor.ch),
6872 Pos(range.head.line + distance, range.head.ch)
6873 ); }), doc.sel.primIndex);
6874 if (doc.cm) {
6875 regChange(doc.cm, doc.first, doc.first - distance, distance);
6876 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6877 { regLineChange(doc.cm, l, "gutter"); }
6878 }
6879 }
6880
6881 // More lower-level change function, handling only a single document
6882 // (not linked ones).
6883 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6884 if (doc.cm && !doc.cm.curOp)
6885 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6886
6887 if (change.to.line < doc.first) {
6888 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6889 return
6890 }
6891 if (change.from.line > doc.lastLine()) { return }
6892
6893 // Clip the change to the size of this doc
6894 if (change.from.line < doc.first) {
6895 var shift = change.text.length - 1 - (doc.first - change.from.line);
6896 shiftDoc(doc, shift);
6897 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6898 text: [lst(change.text)], origin: change.origin};
6899 }
6900 var last = doc.lastLine();
6901 if (change.to.line > last) {
6902 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6903 text: [change.text[0]], origin: change.origin};
6904 }
6905
6906 change.removed = getBetween(doc, change.from, change.to);
6907
6908 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6909 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6910 else { updateDoc(doc, change, spans); }
6911 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6912 }
6913
6914 // Handle the interaction of a change to a document with the editor
6915 // that this document is part of.
6916 function makeChangeSingleDocInEditor(cm, change, spans) {
6917 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6918
6919 var recomputeMaxLength = false, checkWidthStart = from.line;
6920 if (!cm.options.lineWrapping) {
6921 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6922 doc.iter(checkWidthStart, to.line + 1, function (line) {
6923 if (line == display.maxLine) {
6924 recomputeMaxLength = true;
6925 return true
6926 }
6927 });
6928 }
6929
6930 if (doc.sel.contains(change.from, change.to) > -1)
6931 { signalCursorActivity(cm); }
6932
6933 updateDoc(doc, change, spans, estimateHeight(cm));
6934
6935 if (!cm.options.lineWrapping) {
6936 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
6937 var len = lineLength(line);
6938 if (len > display.maxLineLength) {
6939 display.maxLine = line;
6940 display.maxLineLength = len;
6941 display.maxLineChanged = true;
6942 recomputeMaxLength = false;
6943 }
6944 });
6945 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
6946 }
6947
6948 retreatFrontier(doc, from.line);
6949 startWorker(cm, 400);
6950
6951 var lendiff = change.text.length - (to.line - from.line) - 1;
6952 // Remember that these lines changed, for updating the display
6953 if (change.full)
6954 { regChange(cm); }
6955 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
6956 { regLineChange(cm, from.line, "text"); }
6957 else
6958 { regChange(cm, from.line, to.line + 1, lendiff); }
6959
6960 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
6961 if (changeHandler || changesHandler) {
6962 var obj = {
6963 from: from, to: to,
6964 text: change.text,
6965 removed: change.removed,
6966 origin: change.origin
6967 };
6968 if (changeHandler) { signalLater(cm, "change", cm, obj); }
6969 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
6970 }
6971 cm.display.selForContextMenu = null;
6972 }
6973
6974 function replaceRange(doc, code, from, to, origin) {
6975 if (!to) { to = from; }
6976 if (cmp(to, from) < 0) { var assign;
6977 (assign = [to, from], from = assign[0], to = assign[1], assign); }
6978 if (typeof code == "string") { code = doc.splitLines(code); }
6979 makeChange(doc, {from: from, to: to, text: code, origin: origin});
6980 }
6981
6982 // Rebasing/resetting history to deal with externally-sourced changes
6983
6984 function rebaseHistSelSingle(pos, from, to, diff) {
6985 if (to < pos.line) {
6986 pos.line += diff;
6987 } else if (from < pos.line) {
6988 pos.line = from;
6989 pos.ch = 0;
6990 }
6991 }
6992
6993 // Tries to rebase an array of history events given a change in the
6994 // document. If the change touches the same lines as the event, the
6995 // event, and everything 'behind' it, is discarded. If the change is
6996 // before the event, the event's positions are updated. Uses a
6997 // copy-on-write scheme for the positions, to avoid having to
6998 // reallocate them all on every rebase, but also avoid problems with
6999 // shared position objects being unsafely updated.
7000 function rebaseHistArray(array, from, to, diff) {
7001 for (var i = 0; i < array.length; ++i) {
7002 var sub = array[i], ok = true;
7003 if (sub.ranges) {
7004 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7005 for (var j = 0; j < sub.ranges.length; j++) {
7006 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7007 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7008 }
7009 continue
7010 }
7011 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
7012 var cur = sub.changes[j$1];
7013 if (to < cur.from.line) {
7014 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7015 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7016 } else if (from <= cur.to.line) {
7017 ok = false;
7018 break
7019 }
7020 }
7021 if (!ok) {
7022 array.splice(0, i + 1);
7023 i = 0;
7024 }
7025 }
7026 }
7027
7028 function rebaseHist(hist, change) {
7029 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7030 rebaseHistArray(hist.done, from, to, diff);
7031 rebaseHistArray(hist.undone, from, to, diff);
7032 }
7033
7034 // Utility for applying a change to a line by handle or number,
7035 // returning the number and optionally registering the line as
7036 // changed.
7037 function changeLine(doc, handle, changeType, op) {
7038 var no = handle, line = handle;
7039 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
7040 else { no = lineNo(handle); }
7041 if (no == null) { return null }
7042 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
7043 return line
7044 }
7045
7046 // The document is represented as a BTree consisting of leaves, with
7047 // chunk of lines in them, and branches, with up to ten leaves or
7048 // other branch nodes below them. The top node is always a branch
7049 // node, and is the document object itself (meaning it has
7050 // additional methods and properties).
7051 //
7052 // All nodes have parent links. The tree is used both to go from
7053 // line numbers to line objects, and to go from objects to numbers.
7054 // It also indexes by height, and is used to convert between height
7055 // and line object, and to find the total height of the document.
7056 //
7057 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7058
7059 function LeafChunk(lines) {
7060 var this$1 = this;
7061
7062 this.lines = lines;
7063 this.parent = null;
7064 var height = 0;
7065 for (var i = 0; i < lines.length; ++i) {
7066 lines[i].parent = this$1;
7067 height += lines[i].height;
7068 }
7069 this.height = height;
7070 }
7071
7072 LeafChunk.prototype = {
7073 chunkSize: function chunkSize() { return this.lines.length },
7074
7075 // Remove the n lines at offset 'at'.
7076 removeInner: function removeInner(at, n) {
7077 var this$1 = this;
7078
7079 for (var i = at, e = at + n; i < e; ++i) {
7080 var line = this$1.lines[i];
7081 this$1.height -= line.height;
7082 cleanUpLine(line);
7083 signalLater(line, "delete");
7084 }
7085 this.lines.splice(at, n);
7086 },
7087
7088 // Helper used to collapse a small branch into a single leaf.
7089 collapse: function collapse(lines) {
7090 lines.push.apply(lines, this.lines);
7091 },
7092
7093 // Insert the given array of lines at offset 'at', count them as
7094 // having the given height.
7095 insertInner: function insertInner(at, lines, height) {
7096 var this$1 = this;
7097
7098 this.height += height;
7099 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7100 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
7101 },
7102
7103 // Used to iterate over a part of the tree.
7104 iterN: function iterN(at, n, op) {
7105 var this$1 = this;
7106
7107 for (var e = at + n; at < e; ++at)
7108 { if (op(this$1.lines[at])) { return true } }
7109 }
7110 };
7111
7112 function BranchChunk(children) {
7113 var this$1 = this;
7114
7115 this.children = children;
7116 var size = 0, height = 0;
7117 for (var i = 0; i < children.length; ++i) {
7118 var ch = children[i];
7119 size += ch.chunkSize(); height += ch.height;
7120 ch.parent = this$1;
7121 }
7122 this.size = size;
7123 this.height = height;
7124 this.parent = null;
7125 }
7126
7127 BranchChunk.prototype = {
7128 chunkSize: function chunkSize() { return this.size },
7129
7130 removeInner: function removeInner(at, n) {
7131 var this$1 = this;
7132
7133 this.size -= n;
7134 for (var i = 0; i < this.children.length; ++i) {
7135 var child = this$1.children[i], sz = child.chunkSize();
7136 if (at < sz) {
7137 var rm = Math.min(n, sz - at), oldHeight = child.height;
7138 child.removeInner(at, rm);
7139 this$1.height -= oldHeight - child.height;
7140 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
7141 if ((n -= rm) == 0) { break }
7142 at = 0;
7143 } else { at -= sz; }
7144 }
7145 // If the result is smaller than 25 lines, ensure that it is a
7146 // single leaf node.
7147 if (this.size - n < 25 &&
7148 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7149 var lines = [];
7150 this.collapse(lines);
7151 this.children = [new LeafChunk(lines)];
7152 this.children[0].parent = this;
7153 }
7154 },
7155
7156 collapse: function collapse(lines) {
7157 var this$1 = this;
7158
7159 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
7160 },
7161
7162 insertInner: function insertInner(at, lines, height) {
7163 var this$1 = this;
7164
7165 this.size += lines.length;
7166 this.height += height;
7167 for (var i = 0; i < this.children.length; ++i) {
7168 var child = this$1.children[i], sz = child.chunkSize();
7169 if (at <= sz) {
7170 child.insertInner(at, lines, height);
7171 if (child.lines && child.lines.length > 50) {
7172 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7173 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7174 var remaining = child.lines.length % 25 + 25;
7175 for (var pos = remaining; pos < child.lines.length;) {
7176 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7177 child.height -= leaf.height;
7178 this$1.children.splice(++i, 0, leaf);
7179 leaf.parent = this$1;
7180 }
7181 child.lines = child.lines.slice(0, remaining);
7182 this$1.maybeSpill();
7183 }
7184 break
7185 }
7186 at -= sz;
7187 }
7188 },
7189
7190 // When a node has grown, check whether it should be split.
7191 maybeSpill: function maybeSpill() {
7192 if (this.children.length <= 10) { return }
7193 var me = this;
7194 do {
7195 var spilled = me.children.splice(me.children.length - 5, 5);
7196 var sibling = new BranchChunk(spilled);
7197 if (!me.parent) { // Become the parent node
7198 var copy = new BranchChunk(me.children);
7199 copy.parent = me;
7200 me.children = [copy, sibling];
7201 me = copy;
7202 } else {
7203 me.size -= sibling.size;
7204 me.height -= sibling.height;
7205 var myIndex = indexOf(me.parent.children, me);
7206 me.parent.children.splice(myIndex + 1, 0, sibling);
7207 }
7208 sibling.parent = me.parent;
7209 } while (me.children.length > 10)
7210 me.parent.maybeSpill();
7211 },
7212
7213 iterN: function iterN(at, n, op) {
7214 var this$1 = this;
7215
7216 for (var i = 0; i < this.children.length; ++i) {
7217 var child = this$1.children[i], sz = child.chunkSize();
7218 if (at < sz) {
7219 var used = Math.min(n, sz - at);
7220 if (child.iterN(at, used, op)) { return true }
7221 if ((n -= used) == 0) { break }
7222 at = 0;
7223 } else { at -= sz; }
7224 }
7225 }
7226 };
7227
7228 // Line widgets are block elements displayed above or below a line.
7229
7230 var LineWidget = function(doc, node, options) {
7231 var this$1 = this;
7232
7233 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7234 { this$1[opt] = options[opt]; } } }
7235 this.doc = doc;
7236 this.node = node;
7237 };
7238
7239 LineWidget.prototype.clear = function () {
7240 var this$1 = this;
7241
7242 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7243 if (no == null || !ws) { return }
7244 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7245 if (!ws.length) { line.widgets = null; }
7246 var height = widgetHeight(this);
7247 updateLineHeight(line, Math.max(0, line.height - height));
7248 if (cm) {
7249 runInOp(cm, function () {
7250 adjustScrollWhenAboveVisible(cm, line, -height);
7251 regLineChange(cm, no, "widget");
7252 });
7253 signalLater(cm, "lineWidgetCleared", cm, this, no);
7254 }
7255 };
7256
7257 LineWidget.prototype.changed = function () {
7258 var this$1 = this;
7259
7260 var oldH = this.height, cm = this.doc.cm, line = this.line;
7261 this.height = null;
7262 var diff = widgetHeight(this) - oldH;
7263 if (!diff) { return }
7264 updateLineHeight(line, line.height + diff);
7265 if (cm) {
7266 runInOp(cm, function () {
7267 cm.curOp.forceUpdate = true;
7268 adjustScrollWhenAboveVisible(cm, line, diff);
7269 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7270 });
7271 }
7272 };
7273 eventMixin(LineWidget);
7274
7275 function adjustScrollWhenAboveVisible(cm, line, diff) {
7276 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7277 { addToScrollTop(cm, diff); }
7278 }
7279
7280 function addLineWidget(doc, handle, node, options) {
7281 var widget = new LineWidget(doc, node, options);
7282 var cm = doc.cm;
7283 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7284 changeLine(doc, handle, "widget", function (line) {
7285 var widgets = line.widgets || (line.widgets = []);
7286 if (widget.insertAt == null) { widgets.push(widget); }
7287 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7288 widget.line = line;
7289 if (cm && !lineIsHidden(doc, line)) {
7290 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7291 updateLineHeight(line, line.height + widgetHeight(widget));
7292 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7293 cm.curOp.forceUpdate = true;
7294 }
7295 return true
7296 });
7297 signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle));
7298 return widget
7299 }
7300
7301 // TEXTMARKERS
7302
7303 // Created with markText and setBookmark methods. A TextMarker is a
7304 // handle that can be used to clear or find a marked position in the
7305 // document. Line objects hold arrays (markedSpans) containing
7306 // {from, to, marker} object pointing to such marker objects, and
7307 // indicating that such a marker is present on that line. Multiple
7308 // lines may point to the same marker when it spans across lines.
7309 // The spans will have null for their from/to properties when the
7310 // marker continues beyond the start/end of the line. Markers have
7311 // links back to the lines they currently touch.
7312
7313 // Collapsed markers have unique ids, in order to be able to order
7314 // them, which is needed for uniquely determining an outer marker
7315 // when they overlap (they may nest, but not partially overlap).
7316 var nextMarkerId = 0;
7317
7318 var TextMarker = function(doc, type) {
7319 this.lines = [];
7320 this.type = type;
7321 this.doc = doc;
7322 this.id = ++nextMarkerId;
7323 };
7324
7325 // Clear the marker.
7326 TextMarker.prototype.clear = function () {
7327 var this$1 = this;
7328
7329 if (this.explicitlyCleared) { return }
7330 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7331 if (withOp) { startOperation(cm); }
7332 if (hasHandler(this, "clear")) {
7333 var found = this.find();
7334 if (found) { signalLater(this, "clear", found.from, found.to); }
7335 }
7336 var min = null, max = null;
7337 for (var i = 0; i < this.lines.length; ++i) {
7338 var line = this$1.lines[i];
7339 var span = getMarkedSpanFor(line.markedSpans, this$1);
7340 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7341 else if (cm) {
7342 if (span.to != null) { max = lineNo(line); }
7343 if (span.from != null) { min = lineNo(line); }
7344 }
7345 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7346 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7347 { updateLineHeight(line, textHeight(cm.display)); }
7348 }
7349 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7350 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7351 if (len > cm.display.maxLineLength) {
7352 cm.display.maxLine = visual;
7353 cm.display.maxLineLength = len;
7354 cm.display.maxLineChanged = true;
7355 }
7356 } }
7357
7358 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7359 this.lines.length = 0;
7360 this.explicitlyCleared = true;
7361 if (this.atomic && this.doc.cantEdit) {
7362 this.doc.cantEdit = false;
7363 if (cm) { reCheckSelection(cm.doc); }
7364 }
7365 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7366 if (withOp) { endOperation(cm); }
7367 if (this.parent) { this.parent.clear(); }
7368 };
7369
7370 // Find the position of the marker in the document. Returns a {from,
7371 // to} object by default. Side can be passed to get a specific side
7372 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7373 // Pos objects returned contain a line object, rather than a line
7374 // number (used to prevent looking up the same line twice).
7375 TextMarker.prototype.find = function (side, lineObj) {
7376 var this$1 = this;
7377
7378 if (side == null && this.type == "bookmark") { side = 1; }
7379 var from, to;
7380 for (var i = 0; i < this.lines.length; ++i) {
7381 var line = this$1.lines[i];
7382 var span = getMarkedSpanFor(line.markedSpans, this$1);
7383 if (span.from != null) {
7384 from = Pos(lineObj ? line : lineNo(line), span.from);
7385 if (side == -1) { return from }
7386 }
7387 if (span.to != null) {
7388 to = Pos(lineObj ? line : lineNo(line), span.to);
7389 if (side == 1) { return to }
7390 }
7391 }
7392 return from && {from: from, to: to}
7393 };
7394
7395 // Signals that the marker's widget changed, and surrounding layout
7396 // should be recomputed.
7397 TextMarker.prototype.changed = function () {
7398 var this$1 = this;
7399
7400 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7401 if (!pos || !cm) { return }
7402 runInOp(cm, function () {
7403 var line = pos.line, lineN = lineNo(pos.line);
7404 var view = findViewForLine(cm, lineN);
7405 if (view) {
7406 clearLineMeasurementCacheFor(view);
7407 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7408 }
7409 cm.curOp.updateMaxLine = true;
7410 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7411 var oldHeight = widget.height;
7412 widget.height = null;
7413 var dHeight = widgetHeight(widget) - oldHeight;
7414 if (dHeight)
7415 { updateLineHeight(line, line.height + dHeight); }
7416 }
7417 signalLater(cm, "markerChanged", cm, this$1);
7418 });
7419 };
7420
7421 TextMarker.prototype.attachLine = function (line) {
7422 if (!this.lines.length && this.doc.cm) {
7423 var op = this.doc.cm.curOp;
7424 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7425 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7426 }
7427 this.lines.push(line);
7428 };
7429
7430 TextMarker.prototype.detachLine = function (line) {
7431 this.lines.splice(indexOf(this.lines, line), 1);
7432 if (!this.lines.length && this.doc.cm) {
7433 var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7434 }
7435 };
7436 eventMixin(TextMarker);
7437
7438 // Create a marker, wire it up to the right lines, and
7439 function markText(doc, from, to, options, type) {
7440 // Shared markers (across linked documents) are handled separately
7441 // (markTextShared will call out to this again, once per
7442 // document).
7443 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7444 // Ensure we are in an operation.
7445 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7446
7447 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7448 if (options) { copyObj(options, marker, false); }
7449 // Don't connect empty markers unless clearWhenEmpty is false
7450 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7451 { return marker }
7452 if (marker.replacedWith) {
7453 // Showing up as a widget implies collapsed (widget replaces text)
7454 marker.collapsed = true;
7455 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7456 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7457 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7458 }
7459 if (marker.collapsed) {
7460 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7461 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7462 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7463 seeCollapsedSpans();
7464 }
7465
7466 if (marker.addToHistory)
7467 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7468
7469 var curLine = from.line, cm = doc.cm, updateMaxLine;
7470 doc.iter(curLine, to.line + 1, function (line) {
7471 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7472 { updateMaxLine = true; }
7473 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7474 addMarkedSpan(line, new MarkedSpan(marker,
7475 curLine == from.line ? from.ch : null,
7476 curLine == to.line ? to.ch : null));
7477 ++curLine;
7478 });
7479 // lineIsHidden depends on the presence of the spans, so needs a second pass
7480 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7481 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7482 }); }
7483
7484 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7485
7486 if (marker.readOnly) {
7487 seeReadOnlySpans();
7488 if (doc.history.done.length || doc.history.undone.length)
7489 { doc.clearHistory(); }
7490 }
7491 if (marker.collapsed) {
7492 marker.id = ++nextMarkerId;
7493 marker.atomic = true;
7494 }
7495 if (cm) {
7496 // Sync editor state
7497 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7498 if (marker.collapsed)
7499 { regChange(cm, from.line, to.line + 1); }
7500 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
7501 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7502 if (marker.atomic) { reCheckSelection(cm.doc); }
7503 signalLater(cm, "markerAdded", cm, marker);
7504 }
7505 return marker
7506 }
7507
7508 // SHARED TEXTMARKERS
7509
7510 // A shared marker spans multiple linked documents. It is
7511 // implemented as a meta-marker-object controlling multiple normal
7512 // markers.
7513 var SharedTextMarker = function(markers, primary) {
7514 var this$1 = this;
7515
7516 this.markers = markers;
7517 this.primary = primary;
7518 for (var i = 0; i < markers.length; ++i)
7519 { markers[i].parent = this$1; }
7520 };
7521
7522 SharedTextMarker.prototype.clear = function () {
7523 var this$1 = this;
7524
7525 if (this.explicitlyCleared) { return }
7526 this.explicitlyCleared = true;
7527 for (var i = 0; i < this.markers.length; ++i)
7528 { this$1.markers[i].clear(); }
7529 signalLater(this, "clear");
7530 };
7531
7532 SharedTextMarker.prototype.find = function (side, lineObj) {
7533 return this.primary.find(side, lineObj)
7534 };
7535 eventMixin(SharedTextMarker);
7536
7537 function markTextShared(doc, from, to, options, type) {
7538 options = copyObj(options);
7539 options.shared = false;
7540 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7541 var widget = options.widgetNode;
7542 linkedDocs(doc, function (doc) {
7543 if (widget) { options.widgetNode = widget.cloneNode(true); }
7544 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7545 for (var i = 0; i < doc.linked.length; ++i)
7546 { if (doc.linked[i].isParent) { return } }
7547 primary = lst(markers);
7548 });
7549 return new SharedTextMarker(markers, primary)
7550 }
7551
7552 function findSharedMarkers(doc) {
7553 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7554 }
7555
7556 function copySharedMarkers(doc, markers) {
7557 for (var i = 0; i < markers.length; i++) {
7558 var marker = markers[i], pos = marker.find();
7559 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7560 if (cmp(mFrom, mTo)) {
7561 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7562 marker.markers.push(subMark);
7563 subMark.parent = marker;
7564 }
7565 }
7566 }
7567
7568 function detachSharedMarkers(markers) {
7569 var loop = function ( i ) {
7570 var marker = markers[i], linked = [marker.primary.doc];
7571 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7572 for (var j = 0; j < marker.markers.length; j++) {
7573 var subMarker = marker.markers[j];
7574 if (indexOf(linked, subMarker.doc) == -1) {
7575 subMarker.parent = null;
7576 marker.markers.splice(j--, 1);
7577 }
7578 }
7579 };
7580
7581 for (var i = 0; i < markers.length; i++) loop( i );
7582 }
7583
7584 var nextDocId = 0;
7585 var Doc = function(text, mode, firstLine, lineSep, direction) {
7586 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7587 if (firstLine == null) { firstLine = 0; }
7588
7589 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7590 this.first = firstLine;
7591 this.scrollTop = this.scrollLeft = 0;
7592 this.cantEdit = false;
7593 this.cleanGeneration = 1;
7594 this.modeFrontier = this.highlightFrontier = firstLine;
7595 var start = Pos(firstLine, 0);
7596 this.sel = simpleSelection(start);
7597 this.history = new History(null);
7598 this.id = ++nextDocId;
7599 this.modeOption = mode;
7600 this.lineSep = lineSep;
7601 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7602 this.extend = false;
7603
7604 if (typeof text == "string") { text = this.splitLines(text); }
7605 updateDoc(this, {from: start, to: start, text: text});
7606 setSelection(this, simpleSelection(start), sel_dontScroll);
7607 };
7608
7609 Doc.prototype = createObj(BranchChunk.prototype, {
7610 constructor: Doc,
7611 // Iterate over the document. Supports two forms -- with only one
7612 // argument, it calls that for each line in the document. With
7613 // three, it iterates over the range given by the first two (with
7614 // the second being non-inclusive).
7615 iter: function(from, to, op) {
7616 if (op) { this.iterN(from - this.first, to - from, op); }
7617 else { this.iterN(this.first, this.first + this.size, from); }
7618 },
7619
7620 // Non-public interface for adding and removing lines.
7621 insert: function(at, lines) {
7622 var height = 0;
7623 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7624 this.insertInner(at - this.first, lines, height);
7625 },
7626 remove: function(at, n) { this.removeInner(at - this.first, n); },
7627
7628 // From here, the methods are part of the public interface. Most
7629 // are also available from CodeMirror (editor) instances.
7630
7631 getValue: function(lineSep) {
7632 var lines = getLines(this, this.first, this.first + this.size);
7633 if (lineSep === false) { return lines }
7634 return lines.join(lineSep || this.lineSeparator())
7635 },
7636 setValue: docMethodOp(function(code) {
7637 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7638 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7639 text: this.splitLines(code), origin: "setValue", full: true}, true);
7640 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7641 setSelection(this, simpleSelection(top), sel_dontScroll);
7642 }),
7643 replaceRange: function(code, from, to, origin) {
7644 from = clipPos(this, from);
7645 to = to ? clipPos(this, to) : from;
7646 replaceRange(this, code, from, to, origin);
7647 },
7648 getRange: function(from, to, lineSep) {
7649 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7650 if (lineSep === false) { return lines }
7651 return lines.join(lineSep || this.lineSeparator())
7652 },
7653
7654 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7655
7656 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7657 getLineNumber: function(line) {return lineNo(line)},
7658
7659 getLineHandleVisualStart: function(line) {
7660 if (typeof line == "number") { line = getLine(this, line); }
7661 return visualLine(line)
7662 },
7663
7664 lineCount: function() {return this.size},
7665 firstLine: function() {return this.first},
7666 lastLine: function() {return this.first + this.size - 1},
7667
7668 clipPos: function(pos) {return clipPos(this, pos)},
7669
7670 getCursor: function(start) {
7671 var range$$1 = this.sel.primary(), pos;
7672 if (start == null || start == "head") { pos = range$$1.head; }
7673 else if (start == "anchor") { pos = range$$1.anchor; }
7674 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7675 else { pos = range$$1.from(); }
7676 return pos
7677 },
7678 listSelections: function() { return this.sel.ranges },
7679 somethingSelected: function() {return this.sel.somethingSelected()},
7680
7681 setCursor: docMethodOp(function(line, ch, options) {
7682 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7683 }),
7684 setSelection: docMethodOp(function(anchor, head, options) {
7685 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7686 }),
7687 extendSelection: docMethodOp(function(head, other, options) {
7688 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7689 }),
7690 extendSelections: docMethodOp(function(heads, options) {
7691 extendSelections(this, clipPosArray(this, heads), options);
7692 }),
7693 extendSelectionsBy: docMethodOp(function(f, options) {
7694 var heads = map(this.sel.ranges, f);
7695 extendSelections(this, clipPosArray(this, heads), options);
7696 }),
7697 setSelections: docMethodOp(function(ranges, primary, options) {
7698 var this$1 = this;
7699
7700 if (!ranges.length) { return }
7701 var out = [];
7702 for (var i = 0; i < ranges.length; i++)
7703 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7704 clipPos(this$1, ranges[i].head)); }
7705 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7706 setSelection(this, normalizeSelection(out, primary), options);
7707 }),
7708 addSelection: docMethodOp(function(anchor, head, options) {
7709 var ranges = this.sel.ranges.slice(0);
7710 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7711 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7712 }),
7713
7714 getSelection: function(lineSep) {
7715 var this$1 = this;
7716
7717 var ranges = this.sel.ranges, lines;
7718 for (var i = 0; i < ranges.length; i++) {
7719 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7720 lines = lines ? lines.concat(sel) : sel;
7721 }
7722 if (lineSep === false) { return lines }
7723 else { return lines.join(lineSep || this.lineSeparator()) }
7724 },
7725 getSelections: function(lineSep) {
7726 var this$1 = this;
7727
7728 var parts = [], ranges = this.sel.ranges;
7729 for (var i = 0; i < ranges.length; i++) {
7730 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7731 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7732 parts[i] = sel;
7733 }
7734 return parts
7735 },
7736 replaceSelection: function(code, collapse, origin) {
7737 var dup = [];
7738 for (var i = 0; i < this.sel.ranges.length; i++)
7739 { dup[i] = code; }
7740 this.replaceSelections(dup, collapse, origin || "+input");
7741 },
7742 replaceSelections: docMethodOp(function(code, collapse, origin) {
7743 var this$1 = this;
7744
7745 var changes = [], sel = this.sel;
7746 for (var i = 0; i < sel.ranges.length; i++) {
7747 var range$$1 = sel.ranges[i];
7748 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7749 }
7750 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7751 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7752 { makeChange(this$1, changes[i$1]); }
7753 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7754 else if (this.cm) { ensureCursorVisible(this.cm); }
7755 }),
7756 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7757 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7758 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7759 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7760
7761 setExtending: function(val) {this.extend = val;},
7762 getExtending: function() {return this.extend},
7763
7764 historySize: function() {
7765 var hist = this.history, done = 0, undone = 0;
7766 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7767 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7768 return {undo: done, redo: undone}
7769 },
7770 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7771
7772 markClean: function() {
7773 this.cleanGeneration = this.changeGeneration(true);
7774 },
7775 changeGeneration: function(forceSplit) {
7776 if (forceSplit)
7777 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7778 return this.history.generation
7779 },
7780 isClean: function (gen) {
7781 return this.history.generation == (gen || this.cleanGeneration)
7782 },
7783
7784 getHistory: function() {
7785 return {done: copyHistoryArray(this.history.done),
7786 undone: copyHistoryArray(this.history.undone)}
7787 },
7788 setHistory: function(histData) {
7789 var hist = this.history = new History(this.history.maxGeneration);
7790 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7791 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7792 },
7793
7794 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7795 return changeLine(this, line, "gutter", function (line) {
7796 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7797 markers[gutterID] = value;
7798 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7799 return true
7800 })
7801 }),
7802
7803 clearGutter: docMethodOp(function(gutterID) {
7804 var this$1 = this;
7805
7806 this.iter(function (line) {
7807 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7808 changeLine(this$1, line, "gutter", function () {
7809 line.gutterMarkers[gutterID] = null;
7810 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7811 return true
7812 });
7813 }
7814 });
7815 }),
7816
7817 lineInfo: function(line) {
7818 var n;
7819 if (typeof line == "number") {
7820 if (!isLine(this, line)) { return null }
7821 n = line;
7822 line = getLine(this, line);
7823 if (!line) { return null }
7824 } else {
7825 n = lineNo(line);
7826 if (n == null) { return null }
7827 }
7828 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7829 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7830 widgets: line.widgets}
7831 },
7832
7833 addLineClass: docMethodOp(function(handle, where, cls) {
7834 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7835 var prop = where == "text" ? "textClass"
7836 : where == "background" ? "bgClass"
7837 : where == "gutter" ? "gutterClass" : "wrapClass";
7838 if (!line[prop]) { line[prop] = cls; }
7839 else if (classTest(cls).test(line[prop])) { return false }
7840 else { line[prop] += " " + cls; }
7841 return true
7842 })
7843 }),
7844 removeLineClass: docMethodOp(function(handle, where, cls) {
7845 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7846 var prop = where == "text" ? "textClass"
7847 : where == "background" ? "bgClass"
7848 : where == "gutter" ? "gutterClass" : "wrapClass";
7849 var cur = line[prop];
7850 if (!cur) { return false }
7851 else if (cls == null) { line[prop] = null; }
7852 else {
7853 var found = cur.match(classTest(cls));
7854 if (!found) { return false }
7855 var end = found.index + found[0].length;
7856 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7857 }
7858 return true
7859 })
7860 }),
7861
7862 addLineWidget: docMethodOp(function(handle, node, options) {
7863 return addLineWidget(this, handle, node, options)
7864 }),
7865 removeLineWidget: function(widget) { widget.clear(); },
7866
7867 markText: function(from, to, options) {
7868 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7869 },
7870 setBookmark: function(pos, options) {
7871 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7872 insertLeft: options && options.insertLeft,
7873 clearWhenEmpty: false, shared: options && options.shared,
7874 handleMouseEvents: options && options.handleMouseEvents};
7875 pos = clipPos(this, pos);
7876 return markText(this, pos, pos, realOpts, "bookmark")
7877 },
7878 findMarksAt: function(pos) {
7879 pos = clipPos(this, pos);
7880 var markers = [], spans = getLine(this, pos.line).markedSpans;
7881 if (spans) { for (var i = 0; i < spans.length; ++i) {
7882 var span = spans[i];
7883 if ((span.from == null || span.from <= pos.ch) &&
7884 (span.to == null || span.to >= pos.ch))
7885 { markers.push(span.marker.parent || span.marker); }
7886 } }
7887 return markers
7888 },
7889 findMarks: function(from, to, filter) {
7890 from = clipPos(this, from); to = clipPos(this, to);
7891 var found = [], lineNo$$1 = from.line;
7892 this.iter(from.line, to.line + 1, function (line) {
7893 var spans = line.markedSpans;
7894 if (spans) { for (var i = 0; i < spans.length; i++) {
7895 var span = spans[i];
7896 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7897 span.from == null && lineNo$$1 != from.line ||
7898 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7899 (!filter || filter(span.marker)))
7900 { found.push(span.marker.parent || span.marker); }
7901 } }
7902 ++lineNo$$1;
7903 });
7904 return found
7905 },
7906 getAllMarks: function() {
7907 var markers = [];
7908 this.iter(function (line) {
7909 var sps = line.markedSpans;
7910 if (sps) { for (var i = 0; i < sps.length; ++i)
7911 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7912 });
7913 return markers
7914 },
7915
7916 posFromIndex: function(off) {
7917 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7918 this.iter(function (line) {
7919 var sz = line.text.length + sepSize;
7920 if (sz > off) { ch = off; return true }
7921 off -= sz;
7922 ++lineNo$$1;
7923 });
7924 return clipPos(this, Pos(lineNo$$1, ch))
7925 },
7926 indexFromPos: function (coords) {
7927 coords = clipPos(this, coords);
7928 var index = coords.ch;
7929 if (coords.line < this.first || coords.ch < 0) { return 0 }
7930 var sepSize = this.lineSeparator().length;
7931 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
7932 index += line.text.length + sepSize;
7933 });
7934 return index
7935 },
7936
7937 copy: function(copyHistory) {
7938 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7939 this.modeOption, this.first, this.lineSep, this.direction);
7940 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7941 doc.sel = this.sel;
7942 doc.extend = false;
7943 if (copyHistory) {
7944 doc.history.undoDepth = this.history.undoDepth;
7945 doc.setHistory(this.getHistory());
7946 }
7947 return doc
7948 },
7949
7950 linkedDoc: function(options) {
7951 if (!options) { options = {}; }
7952 var from = this.first, to = this.first + this.size;
7953 if (options.from != null && options.from > from) { from = options.from; }
7954 if (options.to != null && options.to < to) { to = options.to; }
7955 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
7956 if (options.sharedHist) { copy.history = this.history
7957 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7958 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7959 copySharedMarkers(copy, findSharedMarkers(this));
7960 return copy
7961 },
7962 unlinkDoc: function(other) {
7963 var this$1 = this;
7964
7965 if (other instanceof CodeMirror$1) { other = other.doc; }
7966 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
7967 var link = this$1.linked[i];
7968 if (link.doc != other) { continue }
7969 this$1.linked.splice(i, 1);
7970 other.unlinkDoc(this$1);
7971 detachSharedMarkers(findSharedMarkers(this$1));
7972 break
7973 } }
7974 // If the histories were shared, split them again
7975 if (other.history == this.history) {
7976 var splitIds = [other.id];
7977 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
7978 other.history = new History(null);
7979 other.history.done = copyHistoryArray(this.history.done, splitIds);
7980 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7981 }
7982 },
7983 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7984
7985 getMode: function() {return this.mode},
7986 getEditor: function() {return this.cm},
7987
7988 splitLines: function(str) {
7989 if (this.lineSep) { return str.split(this.lineSep) }
7990 return splitLinesAuto(str)
7991 },
7992 lineSeparator: function() { return this.lineSep || "\n" },
7993
7994 setDirection: docMethodOp(function (dir) {
7995 if (dir != "rtl") { dir = "ltr"; }
7996 if (dir == this.direction) { return }
7997 this.direction = dir;
7998 this.iter(function (line) { return line.order = null; });
7999 if (this.cm) { directionChanged(this.cm); }
8000 })
8001 });
8002
8003 // Public alias.
8004 Doc.prototype.eachLine = Doc.prototype.iter;
8005
8006 // Kludge to work around strange IE behavior where it'll sometimes
8007 // re-fire a series of drag-related events right after the drop (#1551)
8008 var lastDrop = 0;
8009
8010 function onDrop(e) {
8011 var cm = this;
8012 clearDragCursor(cm);
8013 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
8014 { return }
8015 e_preventDefault(e);
8016 if (ie) { lastDrop = +new Date; }
8017 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
8018 if (!pos || cm.isReadOnly()) { return }
8019 // Might be a file drop, in which case we simply extract the text
8020 // and insert it.
8021 if (files && files.length && window.FileReader && window.File) {
8022 var n = files.length, text = Array(n), read = 0;
8023 var loadFile = function (file, i) {
8024 if (cm.options.allowDropFileTypes &&
8025 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
8026 { return }
8027
8028 var reader = new FileReader;
8029 reader.onload = operation(cm, function () {
8030 var content = reader.result;
8031 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
8032 text[i] = content;
8033 if (++read == n) {
8034 pos = clipPos(cm.doc, pos);
8035 var change = {from: pos, to: pos,
8036 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
8037 origin: "paste"};
8038 makeChange(cm.doc, change);
8039 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
8040 }
8041 });
8042 reader.readAsText(file);
8043 };
8044 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
8045 } else { // Normal drop
8046 // Don't do a replace if the drop happened inside of the selected text.
8047 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
8048 cm.state.draggingText(e);
8049 // Ensure the editor is re-focused
8050 setTimeout(function () { return cm.display.input.focus(); }, 20);
8051 return
8052 }
8053 try {
8054 var text$1 = e.dataTransfer.getData("Text");
8055 if (text$1) {
8056 var selected;
8057 if (cm.state.draggingText && !cm.state.draggingText.copy)
8058 { selected = cm.listSelections(); }
8059 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
8060 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
8061 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
8062 cm.replaceSelection(text$1, "around", "paste");
8063 cm.display.input.focus();
8064 }
8065 }
8066 catch(e){}
8067 }
8068 }
8069
8070 function onDragStart(cm, e) {
8071 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
8072 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
8073
8074 e.dataTransfer.setData("Text", cm.getSelection());
8075 e.dataTransfer.effectAllowed = "copyMove";
8076
8077 // Use dummy image instead of default browsers image.
8078 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
8079 if (e.dataTransfer.setDragImage && !safari) {
8080 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
8081 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
8082 if (presto) {
8083 img.width = img.height = 1;
8084 cm.display.wrapper.appendChild(img);
8085 // Force a relayout, or Opera won't use our image for some obscure reason
8086 img._top = img.offsetTop;
8087 }
8088 e.dataTransfer.setDragImage(img, 0, 0);
8089 if (presto) { img.parentNode.removeChild(img); }
8090 }
8091 }
8092
8093 function onDragOver(cm, e) {
8094 var pos = posFromMouse(cm, e);
8095 if (!pos) { return }
8096 var frag = document.createDocumentFragment();
8097 drawSelectionCursor(cm, pos, frag);
8098 if (!cm.display.dragCursor) {
8099 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8100 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8101 }
8102 removeChildrenAndAdd(cm.display.dragCursor, frag);
8103 }
8104
8105 function clearDragCursor(cm) {
8106 if (cm.display.dragCursor) {
8107 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8108 cm.display.dragCursor = null;
8109 }
8110 }
8111
8112 // These must be handled carefully, because naively registering a
8113 // handler for each editor will cause the editors to never be
8114 // garbage collected.
8115
8116 function forEachCodeMirror(f) {
8117 if (!document.getElementsByClassName) { return }
8118 var byClass = document.getElementsByClassName("CodeMirror");
8119 for (var i = 0; i < byClass.length; i++) {
8120 var cm = byClass[i].CodeMirror;
8121 if (cm) { f(cm); }
8122 }
8123 }
8124
8125 var globalsRegistered = false;
8126 function ensureGlobalHandlers() {
8127 if (globalsRegistered) { return }
8128 registerGlobalHandlers();
8129 globalsRegistered = true;
8130 }
8131 function registerGlobalHandlers() {
8132 // When the window resizes, we need to refresh active editors.
8133 var resizeTimer;
8134 on(window, "resize", function () {
8135 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8136 resizeTimer = null;
8137 forEachCodeMirror(onResize);
8138 }, 100); }
8139 });
8140 // When the window loses focus, we want to show the editor as blurred
8141 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8142 }
8143 // Called when the window resizes
8144 function onResize(cm) {
8145 var d = cm.display;
8146 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
8147 { return }
8148 // Might be a text scaling operation, clear size caches.
8149 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8150 d.scrollbarsClipped = false;
8151 cm.setSize();
8152 }
8153
8154 var keyNames = {
8155 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8156 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8157 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8158 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8159 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
8160 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8161 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8162 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8163 };
8164
8165 // Number keys
8166 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8167 // Alphabetic keys
8168 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8169 // Function keys
8170 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8171
8172 var keyMap = {};
8173
8174 keyMap.basic = {
8175 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8176 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8177 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8178 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8179 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8180 "Esc": "singleSelection"
8181 };
8182 // Note that the save and find-related commands aren't defined by
8183 // default. User code or addons can define them. Unknown commands
8184 // are simply ignored.
8185 keyMap.pcDefault = {
8186 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8187 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8188 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8189 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8190 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8191 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8192 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8193 fallthrough: "basic"
8194 };
8195 // Very basic readline/emacs-style bindings, which are standard on Mac.
8196 keyMap.emacsy = {
8197 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8198 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8199 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8200 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8201 "Ctrl-O": "openLine"
8202 };
8203 keyMap.macDefault = {
8204 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8205 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8206 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8207 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8208 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8209 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8210 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8211 fallthrough: ["basic", "emacsy"]
8212 };
8213 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8214
8215 // KEYMAP DISPATCH
8216
8217 function normalizeKeyName(name) {
8218 var parts = name.split(/-(?!$)/);
8219 name = parts[parts.length - 1];
8220 var alt, ctrl, shift, cmd;
8221 for (var i = 0; i < parts.length - 1; i++) {
8222 var mod = parts[i];
8223 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8224 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8225 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8226 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8227 else { throw new Error("Unrecognized modifier name: " + mod) }
8228 }
8229 if (alt) { name = "Alt-" + name; }
8230 if (ctrl) { name = "Ctrl-" + name; }
8231 if (cmd) { name = "Cmd-" + name; }
8232 if (shift) { name = "Shift-" + name; }
8233 return name
8234 }
8235
8236 // This is a kludge to keep keymaps mostly working as raw objects
8237 // (backwards compatibility) while at the same time support features
8238 // like normalization and multi-stroke key bindings. It compiles a
8239 // new normalized keymap, and then updates the old object to reflect
8240 // this.
8241 function normalizeKeyMap(keymap) {
8242 var copy = {};
8243 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8244 var value = keymap[keyname];
8245 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8246 if (value == "...") { delete keymap[keyname]; continue }
8247
8248 var keys = map(keyname.split(" "), normalizeKeyName);
8249 for (var i = 0; i < keys.length; i++) {
8250 var val = (void 0), name = (void 0);
8251 if (i == keys.length - 1) {
8252 name = keys.join(" ");
8253 val = value;
8254 } else {
8255 name = keys.slice(0, i + 1).join(" ");
8256 val = "...";
8257 }
8258 var prev = copy[name];
8259 if (!prev) { copy[name] = val; }
8260 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8261 }
8262 delete keymap[keyname];
8263 } }
8264 for (var prop in copy) { keymap[prop] = copy[prop]; }
8265 return keymap
8266 }
8267
8268 function lookupKey(key, map$$1, handle, context) {
8269 map$$1 = getKeyMap(map$$1);
8270 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8271 if (found === false) { return "nothing" }
8272 if (found === "...") { return "multi" }
8273 if (found != null && handle(found)) { return "handled" }
8274
8275 if (map$$1.fallthrough) {
8276 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8277 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8278 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8279 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8280 if (result) { return result }
8281 }
8282 }
8283 }
8284
8285 // Modifier key presses don't count as 'real' key presses for the
8286 // purpose of keymap fallthrough.
8287 function isModifierKey(value) {
8288 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8289 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8290 }
8291
8292 function addModifierNames(name, event, noShift) {
8293 var base = name;
8294 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8295 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8296 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8297 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8298 return name
8299 }
8300
8301 // Look up the name of a key as indicated by an event object.
8302 function keyName(event, noShift) {
8303 if (presto && event.keyCode == 34 && event["char"]) { return false }
8304 var name = keyNames[event.keyCode];
8305 if (name == null || event.altGraphKey) { return false }
8306 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8307 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8308 if (event.keyCode == 3 && event.code) { name = event.code; }
8309 return addModifierNames(name, event, noShift)
8310 }
8311
8312 function getKeyMap(val) {
8313 return typeof val == "string" ? keyMap[val] : val
8314 }
8315
8316 // Helper for deleting text near the selection(s), used to implement
8317 // backspace, delete, and similar functionality.
8318 function deleteNearSelection(cm, compute) {
8319 var ranges = cm.doc.sel.ranges, kill = [];
8320 // Build up a set of ranges to kill first, merging overlapping
8321 // ranges.
8322 for (var i = 0; i < ranges.length; i++) {
8323 var toKill = compute(ranges[i]);
8324 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8325 var replaced = kill.pop();
8326 if (cmp(replaced.from, toKill.from) < 0) {
8327 toKill.from = replaced.from;
8328 break
8329 }
8330 }
8331 kill.push(toKill);
8332 }
8333 // Next, remove those actual ranges.
8334 runInOp(cm, function () {
8335 for (var i = kill.length - 1; i >= 0; i--)
8336 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8337 ensureCursorVisible(cm);
8338 });
8339 }
8340
8341 function moveCharLogically(line, ch, dir) {
8342 var target = skipExtendingChars(line.text, ch + dir, dir);
8343 return target < 0 || target > line.text.length ? null : target
8344 }
8345
8346 function moveLogically(line, start, dir) {
8347 var ch = moveCharLogically(line, start.ch, dir);
8348 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8349 }
8350
8351 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8352 if (visually) {
8353 var order = getOrder(lineObj, cm.doc.direction);
8354 if (order) {
8355 var part = dir < 0 ? lst(order) : order[0];
8356 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8357 var sticky = moveInStorageOrder ? "after" : "before";
8358 var ch;
8359 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8360 // it could be that the last bidi part is not on the last visual line,
8361 // since visual lines contain content order-consecutive chunks.
8362 // Thus, in rtl, we are looking for the first (content-order) character
8363 // in the rtl chunk that is on the last line (that is, the same line
8364 // as the last (content-order) character).
8365 if (part.level > 0 || cm.doc.direction == "rtl") {
8366 var prep = prepareMeasureForLine(cm, lineObj);
8367 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8368 var targetTop = measureCharPrepared(cm, prep, ch).top;
8369 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8370 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8371 } else { ch = dir < 0 ? part.to : part.from; }
8372 return new Pos(lineNo, ch, sticky)
8373 }
8374 }
8375 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8376 }
8377
8378 function moveVisually(cm, line, start, dir) {
8379 var bidi = getOrder(line, cm.doc.direction);
8380 if (!bidi) { return moveLogically(line, start, dir) }
8381 if (start.ch >= line.text.length) {
8382 start.ch = line.text.length;
8383 start.sticky = "before";
8384 } else if (start.ch <= 0) {
8385 start.ch = 0;
8386 start.sticky = "after";
8387 }
8388 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8389 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8390 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8391 // nothing interesting happens.
8392 return moveLogically(line, start, dir)
8393 }
8394
8395 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8396 var prep;
8397 var getWrappedLineExtent = function (ch) {
8398 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8399 prep = prep || prepareMeasureForLine(cm, line);
8400 return wrappedLineExtentChar(cm, line, prep, ch)
8401 };
8402 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8403
8404 if (cm.doc.direction == "rtl" || part.level == 1) {
8405 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8406 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8407 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8408 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8409 var sticky = moveInStorageOrder ? "before" : "after";
8410 return new Pos(start.line, ch, sticky)
8411 }
8412 }
8413
8414 // Case 3: Could not move within this bidi part in this visual line, so leave
8415 // the current bidi part
8416
8417 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8418 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8419 ? new Pos(start.line, mv(ch, 1), "before")
8420 : new Pos(start.line, ch, "after"); };
8421
8422 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8423 var part = bidi[partPos];
8424 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8425 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8426 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8427 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8428 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8429 }
8430 };
8431
8432 // Case 3a: Look for other bidi parts on the same visual line
8433 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8434 if (res) { return res }
8435
8436 // Case 3b: Look for other bidi parts on the next visual line
8437 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8438 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8439 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8440 if (res) { return res }
8441 }
8442
8443 // Case 4: Nowhere to move
8444 return null
8445 }
8446
8447 // Commands are parameter-less actions that can be performed on an
8448 // editor, mostly used for keybindings.
8449 var commands = {
8450 selectAll: selectAll,
8451 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8452 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8453 if (range.empty()) {
8454 var len = getLine(cm.doc, range.head.line).text.length;
8455 if (range.head.ch == len && range.head.line < cm.lastLine())
8456 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8457 else
8458 { return {from: range.head, to: Pos(range.head.line, len)} }
8459 } else {
8460 return {from: range.from(), to: range.to()}
8461 }
8462 }); },
8463 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8464 from: Pos(range.from().line, 0),
8465 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8466 }); }); },
8467 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8468 from: Pos(range.from().line, 0), to: range.from()
8469 }); }); },
8470 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8471 var top = cm.charCoords(range.head, "div").top + 5;
8472 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8473 return {from: leftPos, to: range.from()}
8474 }); },
8475 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8476 var top = cm.charCoords(range.head, "div").top + 5;
8477 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8478 return {from: range.from(), to: rightPos }
8479 }); },
8480 undo: function (cm) { return cm.undo(); },
8481 redo: function (cm) { return cm.redo(); },
8482 undoSelection: function (cm) { return cm.undoSelection(); },
8483 redoSelection: function (cm) { return cm.redoSelection(); },
8484 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8485 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8486 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8487 {origin: "+move", bias: 1}
8488 ); },
8489 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8490 {origin: "+move", bias: 1}
8491 ); },
8492 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8493 {origin: "+move", bias: -1}
8494 ); },
8495 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8496 var top = cm.cursorCoords(range.head, "div").top + 5;
8497 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8498 }, sel_move); },
8499 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8500 var top = cm.cursorCoords(range.head, "div").top + 5;
8501 return cm.coordsChar({left: 0, top: top}, "div")
8502 }, sel_move); },
8503 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8504 var top = cm.cursorCoords(range.head, "div").top + 5;
8505 var pos = cm.coordsChar({left: 0, top: top}, "div");
8506 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8507 return pos
8508 }, sel_move); },
8509 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8510 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8511 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8512 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8513 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8514 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8515 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8516 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8517 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8518 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8519 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8520 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8521 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8522 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8523 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8524 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8525 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8526 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8527 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8528 indentMore: function (cm) { return cm.indentSelection("add"); },
8529 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8530 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8531 insertSoftTab: function (cm) {
8532 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8533 for (var i = 0; i < ranges.length; i++) {
8534 var pos = ranges[i].from();
8535 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8536 spaces.push(spaceStr(tabSize - col % tabSize));
8537 }
8538 cm.replaceSelections(spaces);
8539 },
8540 defaultTab: function (cm) {
8541 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8542 else { cm.execCommand("insertTab"); }
8543 },
8544 // Swap the two chars left and right of each selection's head.
8545 // Move cursor behind the two swapped characters afterwards.
8546 //
8547 // Doesn't consider line feeds a character.
8548 // Doesn't scan more than one line above to find a character.
8549 // Doesn't do anything on an empty line.
8550 // Doesn't do anything with non-empty selections.
8551 transposeChars: function (cm) { return runInOp(cm, function () {
8552 var ranges = cm.listSelections(), newSel = [];
8553 for (var i = 0; i < ranges.length; i++) {
8554 if (!ranges[i].empty()) { continue }
8555 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8556 if (line) {
8557 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8558 if (cur.ch > 0) {
8559 cur = new Pos(cur.line, cur.ch + 1);
8560 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8561 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8562 } else if (cur.line > cm.doc.first) {
8563 var prev = getLine(cm.doc, cur.line - 1).text;
8564 if (prev) {
8565 cur = new Pos(cur.line, 1);
8566 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8567 prev.charAt(prev.length - 1),
8568 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8569 }
8570 }
8571 }
8572 newSel.push(new Range(cur, cur));
8573 }
8574 cm.setSelections(newSel);
8575 }); },
8576 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8577 var sels = cm.listSelections();
8578 for (var i = sels.length - 1; i >= 0; i--)
8579 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8580 sels = cm.listSelections();
8581 for (var i$1 = 0; i$1 < sels.length; i$1++)
8582 { cm.indentLine(sels[i$1].from().line, null, true); }
8583 ensureCursorVisible(cm);
8584 }); },
8585 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8586 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8587 };
8588
8589
8590 function lineStart(cm, lineN) {
8591 var line = getLine(cm.doc, lineN);
8592 var visual = visualLine(line);
8593 if (visual != line) { lineN = lineNo(visual); }
8594 return endOfLine(true, cm, visual, lineN, 1)
8595 }
8596 function lineEnd(cm, lineN) {
8597 var line = getLine(cm.doc, lineN);
8598 var visual = visualLineEnd(line);
8599 if (visual != line) { lineN = lineNo(visual); }
8600 return endOfLine(true, cm, line, lineN, -1)
8601 }
8602 function lineStartSmart(cm, pos) {
8603 var start = lineStart(cm, pos.line);
8604 var line = getLine(cm.doc, start.line);
8605 var order = getOrder(line, cm.doc.direction);
8606 if (!order || order[0].level == 0) {
8607 var firstNonWS = Math.max(0, line.text.search(/\S/));
8608 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8609 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8610 }
8611 return start
8612 }
8613
8614 // Run a handler that was bound to a key.
8615 function doHandleBinding(cm, bound, dropShift) {
8616 if (typeof bound == "string") {
8617 bound = commands[bound];
8618 if (!bound) { return false }
8619 }
8620 // Ensure previous input has been read, so that the handler sees a
8621 // consistent view of the document
8622 cm.display.input.ensurePolled();
8623 var prevShift = cm.display.shift, done = false;
8624 try {
8625 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8626 if (dropShift) { cm.display.shift = false; }
8627 done = bound(cm) != Pass;
8628 } finally {
8629 cm.display.shift = prevShift;
8630 cm.state.suppressEdits = false;
8631 }
8632 return done
8633 }
8634
8635 function lookupKeyForEditor(cm, name, handle) {
8636 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8637 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8638 if (result) { return result }
8639 }
8640 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8641 || lookupKey(name, cm.options.keyMap, handle, cm)
8642 }
8643
8644 // Note that, despite the name, this function is also used to check
8645 // for bound mouse clicks.
8646
8647 var stopSeq = new Delayed;
8648
8649 function dispatchKey(cm, name, e, handle) {
8650 var seq = cm.state.keySeq;
8651 if (seq) {
8652 if (isModifierKey(name)) { return "handled" }
8653 if (/\'$/.test(name))
8654 { cm.state.keySeq = null; }
8655 else
8656 { stopSeq.set(50, function () {
8657 if (cm.state.keySeq == seq) {
8658 cm.state.keySeq = null;
8659 cm.display.input.reset();
8660 }
8661 }); }
8662 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8663 }
8664 return dispatchKeyInner(cm, name, e, handle)
8665 }
8666
8667 function dispatchKeyInner(cm, name, e, handle) {
8668 var result = lookupKeyForEditor(cm, name, handle);
8669
8670 if (result == "multi")
8671 { cm.state.keySeq = name; }
8672 if (result == "handled")
8673 { signalLater(cm, "keyHandled", cm, name, e); }
8674
8675 if (result == "handled" || result == "multi") {
8676 e_preventDefault(e);
8677 restartBlink(cm);
8678 }
8679
8680 return !!result
8681 }
8682
8683 // Handle a key from the keydown event.
8684 function handleKeyBinding(cm, e) {
8685 var name = keyName(e, true);
8686 if (!name) { return false }
8687
8688 if (e.shiftKey && !cm.state.keySeq) {
8689 // First try to resolve full name (including 'Shift-'). Failing
8690 // that, see if there is a cursor-motion command (starting with
8691 // 'go') bound to the keyname without 'Shift-'.
8692 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8693 || dispatchKey(cm, name, e, function (b) {
8694 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8695 { return doHandleBinding(cm, b) }
8696 })
8697 } else {
8698 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8699 }
8700 }
8701
8702 // Handle a key from the keypress event
8703 function handleCharBinding(cm, e, ch) {
8704 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8705 }
8706
8707 var lastStoppedKey = null;
8708 function onKeyDown(e) {
8709 var cm = this;
8710 cm.curOp.focus = activeElt();
8711 if (signalDOMEvent(cm, e)) { return }
8712 // IE does strange things with escape.
8713 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8714 var code = e.keyCode;
8715 cm.display.shift = code == 16 || e.shiftKey;
8716 var handled = handleKeyBinding(cm, e);
8717 if (presto) {
8718 lastStoppedKey = handled ? code : null;
8719 // Opera has no cut event... we try to at least catch the key combo
8720 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8721 { cm.replaceSelection("", null, "cut"); }
8722 }
8723
8724 // Turn mouse into crosshair when Alt is held on Mac.
8725 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8726 { showCrossHair(cm); }
8727 }
8728
8729 function showCrossHair(cm) {
8730 var lineDiv = cm.display.lineDiv;
8731 addClass(lineDiv, "CodeMirror-crosshair");
8732
8733 function up(e) {
8734 if (e.keyCode == 18 || !e.altKey) {
8735 rmClass(lineDiv, "CodeMirror-crosshair");
8736 off(document, "keyup", up);
8737 off(document, "mouseover", up);
8738 }
8739 }
8740 on(document, "keyup", up);
8741 on(document, "mouseover", up);
8742 }
8743
8744 function onKeyUp(e) {
8745 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8746 signalDOMEvent(this, e);
8747 }
8748
8749 function onKeyPress(e) {
8750 var cm = this;
8751 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8752 var keyCode = e.keyCode, charCode = e.charCode;
8753 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8754 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8755 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8756 // Some browsers fire keypress events for backspace
8757 if (ch == "\x08") { return }
8758 if (handleCharBinding(cm, e, ch)) { return }
8759 cm.display.input.onKeyPress(e);
8760 }
8761
8762 var DOUBLECLICK_DELAY = 400;
8763
8764 var PastClick = function(time, pos, button) {
8765 this.time = time;
8766 this.pos = pos;
8767 this.button = button;
8768 };
8769
8770 PastClick.prototype.compare = function (time, pos, button) {
8771 return this.time + DOUBLECLICK_DELAY > time &&
8772 cmp(pos, this.pos) == 0 && button == this.button
8773 };
8774
8775 var lastClick;
8776 var lastDoubleClick;
8777 function clickRepeat(pos, button) {
8778 var now = +new Date;
8779 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8780 lastClick = lastDoubleClick = null;
8781 return "triple"
8782 } else if (lastClick && lastClick.compare(now, pos, button)) {
8783 lastDoubleClick = new PastClick(now, pos, button);
8784 lastClick = null;
8785 return "double"
8786 } else {
8787 lastClick = new PastClick(now, pos, button);
8788 lastDoubleClick = null;
8789 return "single"
8790 }
8791 }
8792
8793 // A mouse down can be a single click, double click, triple click,
8794 // start of selection drag, start of text drag, new cursor
8795 // (ctrl-click), rectangle drag (alt-drag), or xwin
8796 // middle-click-paste. Or it might be a click on something we should
8797 // not interfere with, such as a scrollbar or widget.
8798 function onMouseDown(e) {
8799 var cm = this, display = cm.display;
8800 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8801 display.input.ensurePolled();
8802 display.shift = e.shiftKey;
8803
8804 if (eventInWidget(display, e)) {
8805 if (!webkit) {
8806 // Briefly turn off draggability, to allow widgets to do
8807 // normal dragging things.
8808 display.scroller.draggable = false;
8809 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8810 }
8811 return
8812 }
8813 if (clickInGutter(cm, e)) { return }
8814 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8815 window.focus();
8816
8817 // #3261: make sure, that we're not starting a second selection
8818 if (button == 1 && cm.state.selectingText)
8819 { cm.state.selectingText(e); }
8820
8821 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8822
8823 if (button == 1) {
8824 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8825 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8826 } else if (button == 2) {
8827 if (pos) { extendSelection(cm.doc, pos); }
8828 setTimeout(function () { return display.input.focus(); }, 20);
8829 } else if (button == 3) {
8830 if (captureRightClick) { onContextMenu(cm, e); }
8831 else { delayBlurEvent(cm); }
8832 }
8833 }
8834
8835 function handleMappedButton(cm, button, pos, repeat, event) {
8836 var name = "Click";
8837 if (repeat == "double") { name = "Double" + name; }
8838 else if (repeat == "triple") { name = "Triple" + name; }
8839 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8840
8841 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8842 if (typeof bound == "string") { bound = commands[bound]; }
8843 if (!bound) { return false }
8844 var done = false;
8845 try {
8846 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8847 done = bound(cm, pos) != Pass;
8848 } finally {
8849 cm.state.suppressEdits = false;
8850 }
8851 return done
8852 })
8853 }
8854
8855 function configureMouse(cm, repeat, event) {
8856 var option = cm.getOption("configureMouse");
8857 var value = option ? option(cm, repeat, event) : {};
8858 if (value.unit == null) {
8859 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8860 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8861 }
8862 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8863 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8864 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8865 return value
8866 }
8867
8868 function leftButtonDown(cm, pos, repeat, event) {
8869 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8870 else { cm.curOp.focus = activeElt(); }
8871
8872 var behavior = configureMouse(cm, repeat, event);
8873
8874 var sel = cm.doc.sel, contained;
8875 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8876 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8877 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8878 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8879 { leftButtonStartDrag(cm, event, pos, behavior); }
8880 else
8881 { leftButtonSelect(cm, event, pos, behavior); }
8882 }
8883
8884 // Start a text drag. When it ends, see if any dragging actually
8885 // happen, and treat as a click if it didn't.
8886 function leftButtonStartDrag(cm, event, pos, behavior) {
8887 var display = cm.display, moved = false;
8888 var dragEnd = operation(cm, function (e) {
8889 if (webkit) { display.scroller.draggable = false; }
8890 cm.state.draggingText = false;
8891 off(document, "mouseup", dragEnd);
8892 off(document, "mousemove", mouseMove);
8893 off(display.scroller, "dragstart", dragStart);
8894 off(display.scroller, "drop", dragEnd);
8895 if (!moved) {
8896 e_preventDefault(e);
8897 if (!behavior.addNew)
8898 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8899 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8900 if (webkit || ie && ie_version == 9)
8901 { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); }
8902 else
8903 { display.input.focus(); }
8904 }
8905 });
8906 var mouseMove = function(e2) {
8907 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8908 };
8909 var dragStart = function () { return moved = true; };
8910 // Let the drag handler handle this.
8911 if (webkit) { display.scroller.draggable = true; }
8912 cm.state.draggingText = dragEnd;
8913 dragEnd.copy = !behavior.moveOnDrag;
8914 // IE's approach to draggable
8915 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8916 on(document, "mouseup", dragEnd);
8917 on(document, "mousemove", mouseMove);
8918 on(display.scroller, "dragstart", dragStart);
8919 on(display.scroller, "drop", dragEnd);
8920
8921 delayBlurEvent(cm);
8922 setTimeout(function () { return display.input.focus(); }, 20);
8923 }
8924
8925 function rangeForUnit(cm, pos, unit) {
8926 if (unit == "char") { return new Range(pos, pos) }
8927 if (unit == "word") { return cm.findWordAt(pos) }
8928 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
8929 var result = unit(cm, pos);
8930 return new Range(result.from, result.to)
8931 }
8932
8933 // Normal selection, as opposed to text dragging.
8934 function leftButtonSelect(cm, event, start, behavior) {
8935 var display = cm.display, doc = cm.doc;
8936 e_preventDefault(event);
8937
8938 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
8939 if (behavior.addNew && !behavior.extend) {
8940 ourIndex = doc.sel.contains(start);
8941 if (ourIndex > -1)
8942 { ourRange = ranges[ourIndex]; }
8943 else
8944 { ourRange = new Range(start, start); }
8945 } else {
8946 ourRange = doc.sel.primary();
8947 ourIndex = doc.sel.primIndex;
8948 }
8949
8950 if (behavior.unit == "rectangle") {
8951 if (!behavior.addNew) { ourRange = new Range(start, start); }
8952 start = posFromMouse(cm, event, true, true);
8953 ourIndex = -1;
8954 } else {
8955 var range$$1 = rangeForUnit(cm, start, behavior.unit);
8956 if (behavior.extend)
8957 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
8958 else
8959 { ourRange = range$$1; }
8960 }
8961
8962 if (!behavior.addNew) {
8963 ourIndex = 0;
8964 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
8965 startSel = doc.sel;
8966 } else if (ourIndex == -1) {
8967 ourIndex = ranges.length;
8968 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
8969 {scroll: false, origin: "*mouse"});
8970 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
8971 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
8972 {scroll: false, origin: "*mouse"});
8973 startSel = doc.sel;
8974 } else {
8975 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
8976 }
8977
8978 var lastPos = start;
8979 function extendTo(pos) {
8980 if (cmp(lastPos, pos) == 0) { return }
8981 lastPos = pos;
8982
8983 if (behavior.unit == "rectangle") {
8984 var ranges = [], tabSize = cm.options.tabSize;
8985 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
8986 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
8987 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
8988 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
8989 line <= end; line++) {
8990 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
8991 if (left == right)
8992 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
8993 else if (text.length > leftPos)
8994 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
8995 }
8996 if (!ranges.length) { ranges.push(new Range(start, start)); }
8997 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
8998 {origin: "*mouse", scroll: false});
8999 cm.scrollIntoView(pos);
9000 } else {
9001 var oldRange = ourRange;
9002 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
9003 var anchor = oldRange.anchor, head;
9004 if (cmp(range$$1.anchor, anchor) > 0) {
9005 head = range$$1.head;
9006 anchor = minPos(oldRange.from(), range$$1.anchor);
9007 } else {
9008 head = range$$1.anchor;
9009 anchor = maxPos(oldRange.to(), range$$1.head);
9010 }
9011 var ranges$1 = startSel.ranges.slice(0);
9012 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
9013 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse);
9014 }
9015 }
9016
9017 var editorSize = display.wrapper.getBoundingClientRect();
9018 // Used to ensure timeout re-tries don't fire when another extend
9019 // happened in the meantime (clearTimeout isn't reliable -- at
9020 // least on Chrome, the timeouts still happen even when cleared,
9021 // if the clear happens after their scheduled firing time).
9022 var counter = 0;
9023
9024 function extend(e) {
9025 var curCount = ++counter;
9026 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
9027 if (!cur) { return }
9028 if (cmp(cur, lastPos) != 0) {
9029 cm.curOp.focus = activeElt();
9030 extendTo(cur);
9031 var visible = visibleLines(display, doc);
9032 if (cur.line >= visible.to || cur.line < visible.from)
9033 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
9034 } else {
9035 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
9036 if (outside) { setTimeout(operation(cm, function () {
9037 if (counter != curCount) { return }
9038 display.scroller.scrollTop += outside;
9039 extend(e);
9040 }), 50); }
9041 }
9042 }
9043
9044 function done(e) {
9045 cm.state.selectingText = false;
9046 counter = Infinity;
9047 e_preventDefault(e);
9048 display.input.focus();
9049 off(document, "mousemove", move);
9050 off(document, "mouseup", up);
9051 doc.history.lastSelOrigin = null;
9052 }
9053
9054 var move = operation(cm, function (e) {
9055 if (!e_button(e)) { done(e); }
9056 else { extend(e); }
9057 });
9058 var up = operation(cm, done);
9059 cm.state.selectingText = up;
9060 on(document, "mousemove", move);
9061 on(document, "mouseup", up);
9062 }
9063
9064 // Used when mouse-selecting to adjust the anchor to the proper side
9065 // of a bidi jump depending on the visual position of the head.
9066 function bidiSimplify(cm, range$$1) {
9067 var anchor = range$$1.anchor;
9068 var head = range$$1.head;
9069 var anchorLine = getLine(cm.doc, anchor.line);
9070 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
9071 var order = getOrder(anchorLine);
9072 if (!order) { return range$$1 }
9073 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
9074 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
9075 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
9076 if (boundary == 0 || boundary == order.length) { return range$$1 }
9077
9078 // Compute the relative visual position of the head compared to the
9079 // anchor (<0 is to the left, >0 to the right)
9080 var leftSide;
9081 if (head.line != anchor.line) {
9082 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
9083 } else {
9084 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
9085 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
9086 if (headIndex == boundary - 1 || headIndex == boundary)
9087 { leftSide = dir < 0; }
9088 else
9089 { leftSide = dir > 0; }
9090 }
9091
9092 var usePart = order[boundary + (leftSide ? -1 : 0)];
9093 var from = leftSide == (usePart.level == 1);
9094 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
9095 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
9096 }
9097
9098
9099 // Determines whether an event happened in the gutter, and fires the
9100 // handlers for the corresponding event.
9101 function gutterEvent(cm, e, type, prevent) {
9102 var mX, mY;
9103 if (e.touches) {
9104 mX = e.touches[0].clientX;
9105 mY = e.touches[0].clientY;
9106 } else {
9107 try { mX = e.clientX; mY = e.clientY; }
9108 catch(e) { return false }
9109 }
9110 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9111 if (prevent) { e_preventDefault(e); }
9112
9113 var display = cm.display;
9114 var lineBox = display.lineDiv.getBoundingClientRect();
9115
9116 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9117 mY -= lineBox.top - display.viewOffset;
9118
9119 for (var i = 0; i < cm.options.gutters.length; ++i) {
9120 var g = display.gutters.childNodes[i];
9121 if (g && g.getBoundingClientRect().right >= mX) {
9122 var line = lineAtHeight(cm.doc, mY);
9123 var gutter = cm.options.gutters[i];
9124 signal(cm, type, cm, line, gutter, e);
9125 return e_defaultPrevented(e)
9126 }
9127 }
9128 }
9129
9130 function clickInGutter(cm, e) {
9131 return gutterEvent(cm, e, "gutterClick", true)
9132 }
9133
9134 // CONTEXT MENU HANDLING
9135
9136 // To make the context menu work, we need to briefly unhide the
9137 // textarea (making it as unobtrusive as possible) to let the
9138 // right-click take effect on it.
9139 function onContextMenu(cm, e) {
9140 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9141 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9142 cm.display.input.onContextMenu(e);
9143 }
9144
9145 function contextMenuInGutter(cm, e) {
9146 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9147 return gutterEvent(cm, e, "gutterContextMenu", false)
9148 }
9149
9150 function themeChanged(cm) {
9151 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9152 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9153 clearCaches(cm);
9154 }
9155
9156 var Init = {toString: function(){return "CodeMirror.Init"}};
9157
9158 var defaults = {};
9159 var optionHandlers = {};
9160
9161 function defineOptions(CodeMirror) {
9162 var optionHandlers = CodeMirror.optionHandlers;
9163
9164 function option(name, deflt, handle, notOnInit) {
9165 CodeMirror.defaults[name] = deflt;
9166 if (handle) { optionHandlers[name] =
9167 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9168 }
9169
9170 CodeMirror.defineOption = option;
9171
9172 // Passed to option handlers when there is no old value.
9173 CodeMirror.Init = Init;
9174
9175 // These two are, on init, called from the constructor because they
9176 // have to be initialized before the editor can start at all.
9177 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9178 option("mode", null, function (cm, val) {
9179 cm.doc.modeOption = val;
9180 loadMode(cm);
9181 }, true);
9182
9183 option("indentUnit", 2, loadMode, true);
9184 option("indentWithTabs", false);
9185 option("smartIndent", true);
9186 option("tabSize", 4, function (cm) {
9187 resetModeState(cm);
9188 clearCaches(cm);
9189 regChange(cm);
9190 }, true);
9191
9192 option("lineSeparator", null, function (cm, val) {
9193 cm.doc.lineSep = val;
9194 if (!val) { return }
9195 var newBreaks = [], lineNo = cm.doc.first;
9196 cm.doc.iter(function (line) {
9197 for (var pos = 0;;) {
9198 var found = line.text.indexOf(val, pos);
9199 if (found == -1) { break }
9200 pos = found + val.length;
9201 newBreaks.push(Pos(lineNo, found));
9202 }
9203 lineNo++;
9204 });
9205 for (var i = newBreaks.length - 1; i >= 0; i--)
9206 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9207 });
9208 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
9209 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9210 if (old != Init) { cm.refresh(); }
9211 });
9212 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9213 option("electricChars", true);
9214 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9215 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9216 }, true);
9217 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9218 option("rtlMoveVisually", !windows);
9219 option("wholeLineUpdateBefore", true);
9220
9221 option("theme", "default", function (cm) {
9222 themeChanged(cm);
9223 guttersChanged(cm);
9224 }, true);
9225 option("keyMap", "default", function (cm, val, old) {
9226 var next = getKeyMap(val);
9227 var prev = old != Init && getKeyMap(old);
9228 if (prev && prev.detach) { prev.detach(cm, next); }
9229 if (next.attach) { next.attach(cm, prev || null); }
9230 });
9231 option("extraKeys", null);
9232 option("configureMouse", null);
9233
9234 option("lineWrapping", false, wrappingChanged, true);
9235 option("gutters", [], function (cm) {
9236 setGuttersForLineNumbers(cm.options);
9237 guttersChanged(cm);
9238 }, true);
9239 option("fixedGutter", true, function (cm, val) {
9240 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9241 cm.refresh();
9242 }, true);
9243 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9244 option("scrollbarStyle", "native", function (cm) {
9245 initScrollbars(cm);
9246 updateScrollbars(cm);
9247 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9248 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9249 }, true);
9250 option("lineNumbers", false, function (cm) {
9251 setGuttersForLineNumbers(cm.options);
9252 guttersChanged(cm);
9253 }, true);
9254 option("firstLineNumber", 1, guttersChanged, true);
9255 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
9256 option("showCursorWhenSelecting", false, updateSelection, true);
9257
9258 option("resetSelectionOnContextMenu", true);
9259 option("lineWiseCopyCut", true);
9260 option("pasteLinesPerSelection", true);
9261
9262 option("readOnly", false, function (cm, val) {
9263 if (val == "nocursor") {
9264 onBlur(cm);
9265 cm.display.input.blur();
9266 }
9267 cm.display.input.readOnlyChanged(val);
9268 });
9269 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9270 option("dragDrop", true, dragDropChanged);
9271 option("allowDropFileTypes", null);
9272
9273 option("cursorBlinkRate", 530);
9274 option("cursorScrollMargin", 0);
9275 option("cursorHeight", 1, updateSelection, true);
9276 option("singleCursorHeightPerLine", true, updateSelection, true);
9277 option("workTime", 100);
9278 option("workDelay", 100);
9279 option("flattenSpans", true, resetModeState, true);
9280 option("addModeClass", false, resetModeState, true);
9281 option("pollInterval", 100);
9282 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9283 option("historyEventDelay", 1250);
9284 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9285 option("maxHighlightLength", 10000, resetModeState, true);
9286 option("moveInputWithCursor", true, function (cm, val) {
9287 if (!val) { cm.display.input.resetPosition(); }
9288 });
9289
9290 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9291 option("autofocus", null);
9292 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9293 }
9294
9295 function guttersChanged(cm) {
9296 updateGutters(cm);
9297 regChange(cm);
9298 alignHorizontally(cm);
9299 }
9300
9301 function dragDropChanged(cm, value, old) {
9302 var wasOn = old && old != Init;
9303 if (!value != !wasOn) {
9304 var funcs = cm.display.dragFunctions;
9305 var toggle = value ? on : off;
9306 toggle(cm.display.scroller, "dragstart", funcs.start);
9307 toggle(cm.display.scroller, "dragenter", funcs.enter);
9308 toggle(cm.display.scroller, "dragover", funcs.over);
9309 toggle(cm.display.scroller, "dragleave", funcs.leave);
9310 toggle(cm.display.scroller, "drop", funcs.drop);
9311 }
9312 }
9313
9314 function wrappingChanged(cm) {
9315 if (cm.options.lineWrapping) {
9316 addClass(cm.display.wrapper, "CodeMirror-wrap");
9317 cm.display.sizer.style.minWidth = "";
9318 cm.display.sizerWidth = null;
9319 } else {
9320 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9321 findMaxLine(cm);
9322 }
9323 estimateLineHeights(cm);
9324 regChange(cm);
9325 clearCaches(cm);
9326 setTimeout(function () { return updateScrollbars(cm); }, 100);
9327 }
9328
9329 // A CodeMirror instance represents an editor. This is the object
9330 // that user code is usually dealing with.
9331
9332 function CodeMirror$1(place, options) {
9333 var this$1 = this;
9334
9335 if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
9336
9337 this.options = options = options ? copyObj(options) : {};
9338 // Determine effective options based on given values and defaults.
9339 copyObj(defaults, options, false);
9340 setGuttersForLineNumbers(options);
9341
9342 var doc = options.value;
9343 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9344 this.doc = doc;
9345
9346 var input = new CodeMirror$1.inputStyles[options.inputStyle](this);
9347 var display = this.display = new Display(place, doc, input);
9348 display.wrapper.CodeMirror = this;
9349 updateGutters(this);
9350 themeChanged(this);
9351 if (options.lineWrapping)
9352 { this.display.wrapper.className += " CodeMirror-wrap"; }
9353 initScrollbars(this);
9354
9355 this.state = {
9356 keyMaps: [], // stores maps added by addKeyMap
9357 overlays: [], // highlighting overlays, as added by addOverlay
9358 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9359 overwrite: false,
9360 delayingBlurEvent: false,
9361 focused: false,
9362 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9363 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
9364 selectingText: false,
9365 draggingText: false,
9366 highlight: new Delayed(), // stores highlight worker timeout
9367 keySeq: null, // Unfinished key sequence
9368 specialChars: null
9369 };
9370
9371 if (options.autofocus && !mobile) { display.input.focus(); }
9372
9373 // Override magic textarea content restore that IE sometimes does
9374 // on our hidden textarea on reload
9375 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9376
9377 registerEventHandlers(this);
9378 ensureGlobalHandlers();
9379
9380 startOperation(this);
9381 this.curOp.forceUpdate = true;
9382 attachDoc(this, doc);
9383
9384 if ((options.autofocus && !mobile) || this.hasFocus())
9385 { setTimeout(bind(onFocus, this), 20); }
9386 else
9387 { onBlur(this); }
9388
9389 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9390 { optionHandlers[opt](this$1, options[opt], Init); } }
9391 maybeUpdateLineNumberWidth(this);
9392 if (options.finishInit) { options.finishInit(this); }
9393 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9394 endOperation(this);
9395 // Suppress optimizelegibility in Webkit, since it breaks text
9396 // measuring on line wrapping boundaries.
9397 if (webkit && options.lineWrapping &&
9398 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9399 { display.lineDiv.style.textRendering = "auto"; }
9400 }
9401
9402 // The default configuration options.
9403 CodeMirror$1.defaults = defaults;
9404 // Functions to run when options are changed.
9405 CodeMirror$1.optionHandlers = optionHandlers;
9406
9407 // Attach the necessary event handlers when initializing the editor
9408 function registerEventHandlers(cm) {
9409 var d = cm.display;
9410 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9411 // Older IE's will not fire a second mousedown for a double click
9412 if (ie && ie_version < 11)
9413 { on(d.scroller, "dblclick", operation(cm, function (e) {
9414 if (signalDOMEvent(cm, e)) { return }
9415 var pos = posFromMouse(cm, e);
9416 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9417 e_preventDefault(e);
9418 var word = cm.findWordAt(pos);
9419 extendSelection(cm.doc, word.anchor, word.head);
9420 })); }
9421 else
9422 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9423 // Some browsers fire contextmenu *after* opening the menu, at
9424 // which point we can't mess with it anymore. Context menu is
9425 // handled in onMouseDown for these browsers.
9426 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); }
9427
9428 // Used to suppress mouse event handling when a touch happens
9429 var touchFinished, prevTouch = {end: 0};
9430 function finishTouch() {
9431 if (d.activeTouch) {
9432 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9433 prevTouch = d.activeTouch;
9434 prevTouch.end = +new Date;
9435 }
9436 }
9437 function isMouseLikeTouchEvent(e) {
9438 if (e.touches.length != 1) { return false }
9439 var touch = e.touches[0];
9440 return touch.radiusX <= 1 && touch.radiusY <= 1
9441 }
9442 function farAway(touch, other) {
9443 if (other.left == null) { return true }
9444 var dx = other.left - touch.left, dy = other.top - touch.top;
9445 return dx * dx + dy * dy > 20 * 20
9446 }
9447 on(d.scroller, "touchstart", function (e) {
9448 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9449 d.input.ensurePolled();
9450 clearTimeout(touchFinished);
9451 var now = +new Date;
9452 d.activeTouch = {start: now, moved: false,
9453 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9454 if (e.touches.length == 1) {
9455 d.activeTouch.left = e.touches[0].pageX;
9456 d.activeTouch.top = e.touches[0].pageY;
9457 }
9458 }
9459 });
9460 on(d.scroller, "touchmove", function () {
9461 if (d.activeTouch) { d.activeTouch.moved = true; }
9462 });
9463 on(d.scroller, "touchend", function (e) {
9464 var touch = d.activeTouch;
9465 if (touch && !eventInWidget(d, e) && touch.left != null &&
9466 !touch.moved && new Date - touch.start < 300) {
9467 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9468 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9469 { range = new Range(pos, pos); }
9470 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9471 { range = cm.findWordAt(pos); }
9472 else // Triple tap
9473 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9474 cm.setSelection(range.anchor, range.head);
9475 cm.focus();
9476 e_preventDefault(e);
9477 }
9478 finishTouch();
9479 });
9480 on(d.scroller, "touchcancel", finishTouch);
9481
9482 // Sync scrolling between fake scrollbars and real scrollable
9483 // area, ensure viewport is updated when scrolling.
9484 on(d.scroller, "scroll", function () {
9485 if (d.scroller.clientHeight) {
9486 updateScrollTop(cm, d.scroller.scrollTop);
9487 setScrollLeft(cm, d.scroller.scrollLeft, true);
9488 signal(cm, "scroll", cm);
9489 }
9490 });
9491
9492 // Listen to wheel events in order to try and update the viewport on time.
9493 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9494 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9495
9496 // Prevent wrapper from ever scrolling
9497 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9498
9499 d.dragFunctions = {
9500 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9501 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9502 start: function (e) { return onDragStart(cm, e); },
9503 drop: operation(cm, onDrop),
9504 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9505 };
9506
9507 var inp = d.input.getField();
9508 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9509 on(inp, "keydown", operation(cm, onKeyDown));
9510 on(inp, "keypress", operation(cm, onKeyPress));
9511 on(inp, "focus", function (e) { return onFocus(cm, e); });
9512 on(inp, "blur", function (e) { return onBlur(cm, e); });
9513 }
9514
9515 var initHooks = [];
9516 CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); };
9517
9518 // Indent the given line. The how parameter can be "smart",
9519 // "add"/null, "subtract", or "prev". When aggressive is false
9520 // (typically set to true for forced single-line indents), empty
9521 // lines are not indented, and places where the mode returns Pass
9522 // are left alone.
9523 function indentLine(cm, n, how, aggressive) {
9524 var doc = cm.doc, state;
9525 if (how == null) { how = "add"; }
9526 if (how == "smart") {
9527 // Fall back to "prev" when the mode doesn't have an indentation
9528 // method.
9529 if (!doc.mode.indent) { how = "prev"; }
9530 else { state = getContextBefore(cm, n).state; }
9531 }
9532
9533 var tabSize = cm.options.tabSize;
9534 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9535 if (line.stateAfter) { line.stateAfter = null; }
9536 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9537 if (!aggressive && !/\S/.test(line.text)) {
9538 indentation = 0;
9539 how = "not";
9540 } else if (how == "smart") {
9541 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9542 if (indentation == Pass || indentation > 150) {
9543 if (!aggressive) { return }
9544 how = "prev";
9545 }
9546 }
9547 if (how == "prev") {
9548 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9549 else { indentation = 0; }
9550 } else if (how == "add") {
9551 indentation = curSpace + cm.options.indentUnit;
9552 } else if (how == "subtract") {
9553 indentation = curSpace - cm.options.indentUnit;
9554 } else if (typeof how == "number") {
9555 indentation = curSpace + how;
9556 }
9557 indentation = Math.max(0, indentation);
9558
9559 var indentString = "", pos = 0;
9560 if (cm.options.indentWithTabs)
9561 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9562 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9563
9564 if (indentString != curSpaceString) {
9565 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9566 line.stateAfter = null;
9567 return true
9568 } else {
9569 // Ensure that, if the cursor was in the whitespace at the start
9570 // of the line, it is moved to the end of that space.
9571 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9572 var range = doc.sel.ranges[i$1];
9573 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9574 var pos$1 = Pos(n, curSpaceString.length);
9575 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9576 break
9577 }
9578 }
9579 }
9580 }
9581
9582 // This will be set to a {lineWise: bool, text: [string]} object, so
9583 // that, when pasting, we know what kind of selections the copied
9584 // text was made out of.
9585 var lastCopied = null;
9586
9587 function setLastCopied(newLastCopied) {
9588 lastCopied = newLastCopied;
9589 }
9590
9591 function applyTextInput(cm, inserted, deleted, sel, origin) {
9592 var doc = cm.doc;
9593 cm.display.shift = false;
9594 if (!sel) { sel = doc.sel; }
9595
9596 var paste = cm.state.pasteIncoming || origin == "paste";
9597 var textLines = splitLinesAuto(inserted), multiPaste = null;
9598 // When pasting N lines into N selections, insert one line per selection
9599 if (paste && sel.ranges.length > 1) {
9600 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9601 if (sel.ranges.length % lastCopied.text.length == 0) {
9602 multiPaste = [];
9603 for (var i = 0; i < lastCopied.text.length; i++)
9604 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9605 }
9606 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9607 multiPaste = map(textLines, function (l) { return [l]; });
9608 }
9609 }
9610
9611 var updateInput;
9612 // Normal behavior is to insert the new text into every selection
9613 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9614 var range$$1 = sel.ranges[i$1];
9615 var from = range$$1.from(), to = range$$1.to();
9616 if (range$$1.empty()) {
9617 if (deleted && deleted > 0) // Handle deletion
9618 { from = Pos(from.line, from.ch - deleted); }
9619 else if (cm.state.overwrite && !paste) // Handle overwrite
9620 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9621 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9622 { from = to = Pos(from.line, 0); }
9623 }
9624 updateInput = cm.curOp.updateInput;
9625 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9626 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
9627 makeChange(cm.doc, changeEvent);
9628 signalLater(cm, "inputRead", cm, changeEvent);
9629 }
9630 if (inserted && !paste)
9631 { triggerElectric(cm, inserted); }
9632
9633 ensureCursorVisible(cm);
9634 cm.curOp.updateInput = updateInput;
9635 cm.curOp.typing = true;
9636 cm.state.pasteIncoming = cm.state.cutIncoming = false;
9637 }
9638
9639 function handlePaste(e, cm) {
9640 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9641 if (pasted) {
9642 e.preventDefault();
9643 if (!cm.isReadOnly() && !cm.options.disableInput)
9644 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9645 return true
9646 }
9647 }
9648
9649 function triggerElectric(cm, inserted) {
9650 // When an 'electric' character is inserted, immediately trigger a reindent
9651 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9652 var sel = cm.doc.sel;
9653
9654 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9655 var range$$1 = sel.ranges[i];
9656 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9657 var mode = cm.getModeAt(range$$1.head);
9658 var indented = false;
9659 if (mode.electricChars) {
9660 for (var j = 0; j < mode.electricChars.length; j++)
9661 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9662 indented = indentLine(cm, range$$1.head.line, "smart");
9663 break
9664 } }
9665 } else if (mode.electricInput) {
9666 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9667 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9668 }
9669 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9670 }
9671 }
9672
9673 function copyableRanges(cm) {
9674 var text = [], ranges = [];
9675 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9676 var line = cm.doc.sel.ranges[i].head.line;
9677 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9678 ranges.push(lineRange);
9679 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9680 }
9681 return {text: text, ranges: ranges}
9682 }
9683
9684 function disableBrowserMagic(field, spellcheck) {
9685 field.setAttribute("autocorrect", "off");
9686 field.setAttribute("autocapitalize", "off");
9687 field.setAttribute("spellcheck", !!spellcheck);
9688 }
9689
9690 function hiddenTextarea() {
9691 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9692 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9693 // The textarea is kept positioned near the cursor to prevent the
9694 // fact that it'll be scrolled into view on input from scrolling
9695 // our fake cursor out of view. On webkit, when wrap=off, paste is
9696 // very slow. So make the area wide instead.
9697 if (webkit) { te.style.width = "1000px"; }
9698 else { te.setAttribute("wrap", "off"); }
9699 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9700 if (ios) { te.style.border = "1px solid black"; }
9701 disableBrowserMagic(te);
9702 return div
9703 }
9704
9705 // The publicly visible API. Note that methodOp(f) means
9706 // 'wrap f in an operation, performed on its `this` parameter'.
9707
9708 // This is not the complete set of editor methods. Most of the
9709 // methods defined on the Doc type are also injected into
9710 // CodeMirror.prototype, for backwards compatibility and
9711 // convenience.
9712
9713 var addEditorMethods = function(CodeMirror) {
9714 var optionHandlers = CodeMirror.optionHandlers;
9715
9716 var helpers = CodeMirror.helpers = {};
9717
9718 CodeMirror.prototype = {
9719 constructor: CodeMirror,
9720 focus: function(){window.focus(); this.display.input.focus();},
9721
9722 setOption: function(option, value) {
9723 var options = this.options, old = options[option];
9724 if (options[option] == value && option != "mode") { return }
9725 options[option] = value;
9726 if (optionHandlers.hasOwnProperty(option))
9727 { operation(this, optionHandlers[option])(this, value, old); }
9728 signal(this, "optionChange", this, option);
9729 },
9730
9731 getOption: function(option) {return this.options[option]},
9732 getDoc: function() {return this.doc},
9733
9734 addKeyMap: function(map$$1, bottom) {
9735 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9736 },
9737 removeKeyMap: function(map$$1) {
9738 var maps = this.state.keyMaps;
9739 for (var i = 0; i < maps.length; ++i)
9740 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9741 maps.splice(i, 1);
9742 return true
9743 } }
9744 },
9745
9746 addOverlay: methodOp(function(spec, options) {
9747 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9748 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9749 insertSorted(this.state.overlays,
9750 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9751 priority: (options && options.priority) || 0},
9752 function (overlay) { return overlay.priority; });
9753 this.state.modeGen++;
9754 regChange(this);
9755 }),
9756 removeOverlay: methodOp(function(spec) {
9757 var this$1 = this;
9758
9759 var overlays = this.state.overlays;
9760 for (var i = 0; i < overlays.length; ++i) {
9761 var cur = overlays[i].modeSpec;
9762 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9763 overlays.splice(i, 1);
9764 this$1.state.modeGen++;
9765 regChange(this$1);
9766 return
9767 }
9768 }
9769 }),
9770
9771 indentLine: methodOp(function(n, dir, aggressive) {
9772 if (typeof dir != "string" && typeof dir != "number") {
9773 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9774 else { dir = dir ? "add" : "subtract"; }
9775 }
9776 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9777 }),
9778 indentSelection: methodOp(function(how) {
9779 var this$1 = this;
9780
9781 var ranges = this.doc.sel.ranges, end = -1;
9782 for (var i = 0; i < ranges.length; i++) {
9783 var range$$1 = ranges[i];
9784 if (!range$$1.empty()) {
9785 var from = range$$1.from(), to = range$$1.to();
9786 var start = Math.max(end, from.line);
9787 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9788 for (var j = start; j < end; ++j)
9789 { indentLine(this$1, j, how); }
9790 var newRanges = this$1.doc.sel.ranges;
9791 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9792 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9793 } else if (range$$1.head.line > end) {
9794 indentLine(this$1, range$$1.head.line, how, true);
9795 end = range$$1.head.line;
9796 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9797 }
9798 }
9799 }),
9800
9801 // Fetch the parser token for a given character. Useful for hacks
9802 // that want to inspect the mode state (say, for completion).
9803 getTokenAt: function(pos, precise) {
9804 return takeToken(this, pos, precise)
9805 },
9806
9807 getLineTokens: function(line, precise) {
9808 return takeToken(this, Pos(line), precise, true)
9809 },
9810
9811 getTokenTypeAt: function(pos) {
9812 pos = clipPos(this.doc, pos);
9813 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9814 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9815 var type;
9816 if (ch == 0) { type = styles[2]; }
9817 else { for (;;) {
9818 var mid = (before + after) >> 1;
9819 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9820 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9821 else { type = styles[mid * 2 + 2]; break }
9822 } }
9823 var cut = type ? type.indexOf("overlay ") : -1;
9824 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9825 },
9826
9827 getModeAt: function(pos) {
9828 var mode = this.doc.mode;
9829 if (!mode.innerMode) { return mode }
9830 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9831 },
9832
9833 getHelper: function(pos, type) {
9834 return this.getHelpers(pos, type)[0]
9835 },
9836
9837 getHelpers: function(pos, type) {
9838 var this$1 = this;
9839
9840 var found = [];
9841 if (!helpers.hasOwnProperty(type)) { return found }
9842 var help = helpers[type], mode = this.getModeAt(pos);
9843 if (typeof mode[type] == "string") {
9844 if (help[mode[type]]) { found.push(help[mode[type]]); }
9845 } else if (mode[type]) {
9846 for (var i = 0; i < mode[type].length; i++) {
9847 var val = help[mode[type][i]];
9848 if (val) { found.push(val); }
9849 }
9850 } else if (mode.helperType && help[mode.helperType]) {
9851 found.push(help[mode.helperType]);
9852 } else if (help[mode.name]) {
9853 found.push(help[mode.name]);
9854 }
9855 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9856 var cur = help._global[i$1];
9857 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9858 { found.push(cur.val); }
9859 }
9860 return found
9861 },
9862
9863 getStateAfter: function(line, precise) {
9864 var doc = this.doc;
9865 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9866 return getContextBefore(this, line + 1, precise).state
9867 },
9868
9869 cursorCoords: function(start, mode) {
9870 var pos, range$$1 = this.doc.sel.primary();
9871 if (start == null) { pos = range$$1.head; }
9872 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9873 else { pos = start ? range$$1.from() : range$$1.to(); }
9874 return cursorCoords(this, pos, mode || "page")
9875 },
9876
9877 charCoords: function(pos, mode) {
9878 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9879 },
9880
9881 coordsChar: function(coords, mode) {
9882 coords = fromCoordSystem(this, coords, mode || "page");
9883 return coordsChar(this, coords.left, coords.top)
9884 },
9885
9886 lineAtHeight: function(height, mode) {
9887 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9888 return lineAtHeight(this.doc, height + this.display.viewOffset)
9889 },
9890 heightAtLine: function(line, mode, includeWidgets) {
9891 var end = false, lineObj;
9892 if (typeof line == "number") {
9893 var last = this.doc.first + this.doc.size - 1;
9894 if (line < this.doc.first) { line = this.doc.first; }
9895 else if (line > last) { line = last; end = true; }
9896 lineObj = getLine(this.doc, line);
9897 } else {
9898 lineObj = line;
9899 }
9900 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9901 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9902 },
9903
9904 defaultTextHeight: function() { return textHeight(this.display) },
9905 defaultCharWidth: function() { return charWidth(this.display) },
9906
9907 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9908
9909 addWidget: function(pos, node, scroll, vert, horiz) {
9910 var display = this.display;
9911 pos = cursorCoords(this, clipPos(this.doc, pos));
9912 var top = pos.bottom, left = pos.left;
9913 node.style.position = "absolute";
9914 node.setAttribute("cm-ignore-events", "true");
9915 this.display.input.setUneditable(node);
9916 display.sizer.appendChild(node);
9917 if (vert == "over") {
9918 top = pos.top;
9919 } else if (vert == "above" || vert == "near") {
9920 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9921 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
9922 // Default to positioning above (if specified and possible); otherwise default to positioning below
9923 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
9924 { top = pos.top - node.offsetHeight; }
9925 else if (pos.bottom + node.offsetHeight <= vspace)
9926 { top = pos.bottom; }
9927 if (left + node.offsetWidth > hspace)
9928 { left = hspace - node.offsetWidth; }
9929 }
9930 node.style.top = top + "px";
9931 node.style.left = node.style.right = "";
9932 if (horiz == "right") {
9933 left = display.sizer.clientWidth - node.offsetWidth;
9934 node.style.right = "0px";
9935 } else {
9936 if (horiz == "left") { left = 0; }
9937 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
9938 node.style.left = left + "px";
9939 }
9940 if (scroll)
9941 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
9942 },
9943
9944 triggerOnKeyDown: methodOp(onKeyDown),
9945 triggerOnKeyPress: methodOp(onKeyPress),
9946 triggerOnKeyUp: onKeyUp,
9947 triggerOnMouseDown: methodOp(onMouseDown),
9948
9949 execCommand: function(cmd) {
9950 if (commands.hasOwnProperty(cmd))
9951 { return commands[cmd].call(null, this) }
9952 },
9953
9954 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
9955
9956 findPosH: function(from, amount, unit, visually) {
9957 var this$1 = this;
9958
9959 var dir = 1;
9960 if (amount < 0) { dir = -1; amount = -amount; }
9961 var cur = clipPos(this.doc, from);
9962 for (var i = 0; i < amount; ++i) {
9963 cur = findPosH(this$1.doc, cur, dir, unit, visually);
9964 if (cur.hitSide) { break }
9965 }
9966 return cur
9967 },
9968
9969 moveH: methodOp(function(dir, unit) {
9970 var this$1 = this;
9971
9972 this.extendSelectionsBy(function (range$$1) {
9973 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
9974 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
9975 else
9976 { return dir < 0 ? range$$1.from() : range$$1.to() }
9977 }, sel_move);
9978 }),
9979
9980 deleteH: methodOp(function(dir, unit) {
9981 var sel = this.doc.sel, doc = this.doc;
9982 if (sel.somethingSelected())
9983 { doc.replaceSelection("", null, "+delete"); }
9984 else
9985 { deleteNearSelection(this, function (range$$1) {
9986 var other = findPosH(doc, range$$1.head, dir, unit, false);
9987 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
9988 }); }
9989 }),
9990
9991 findPosV: function(from, amount, unit, goalColumn) {
9992 var this$1 = this;
9993
9994 var dir = 1, x = goalColumn;
9995 if (amount < 0) { dir = -1; amount = -amount; }
9996 var cur = clipPos(this.doc, from);
9997 for (var i = 0; i < amount; ++i) {
9998 var coords = cursorCoords(this$1, cur, "div");
9999 if (x == null) { x = coords.left; }
10000 else { coords.left = x; }
10001 cur = findPosV(this$1, coords, dir, unit);
10002 if (cur.hitSide) { break }
10003 }
10004 return cur
10005 },
10006
10007 moveV: methodOp(function(dir, unit) {
10008 var this$1 = this;
10009
10010 var doc = this.doc, goals = [];
10011 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
10012 doc.extendSelectionsBy(function (range$$1) {
10013 if (collapse)
10014 { return dir < 0 ? range$$1.from() : range$$1.to() }
10015 var headPos = cursorCoords(this$1, range$$1.head, "div");
10016 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
10017 goals.push(headPos.left);
10018 var pos = findPosV(this$1, headPos, dir, unit);
10019 if (unit == "page" && range$$1 == doc.sel.primary())
10020 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
10021 return pos
10022 }, sel_move);
10023 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
10024 { doc.sel.ranges[i].goalColumn = goals[i]; } }
10025 }),
10026
10027 // Find the word at the given position (as returned by coordsChar).
10028 findWordAt: function(pos) {
10029 var doc = this.doc, line = getLine(doc, pos.line).text;
10030 var start = pos.ch, end = pos.ch;
10031 if (line) {
10032 var helper = this.getHelper(pos, "wordChars");
10033 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
10034 var startChar = line.charAt(start);
10035 var check = isWordChar(startChar, helper)
10036 ? function (ch) { return isWordChar(ch, helper); }
10037 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
10038 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
10039 while (start > 0 && check(line.charAt(start - 1))) { --start; }
10040 while (end < line.length && check(line.charAt(end))) { ++end; }
10041 }
10042 return new Range(Pos(pos.line, start), Pos(pos.line, end))
10043 },
10044
10045 toggleOverwrite: function(value) {
10046 if (value != null && value == this.state.overwrite) { return }
10047 if (this.state.overwrite = !this.state.overwrite)
10048 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10049 else
10050 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10051
10052 signal(this, "overwriteToggle", this, this.state.overwrite);
10053 },
10054 hasFocus: function() { return this.display.input.getField() == activeElt() },
10055 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
10056
10057 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
10058 getScrollInfo: function() {
10059 var scroller = this.display.scroller;
10060 return {left: scroller.scrollLeft, top: scroller.scrollTop,
10061 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
10062 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
10063 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
10064 },
10065
10066 scrollIntoView: methodOp(function(range$$1, margin) {
10067 if (range$$1 == null) {
10068 range$$1 = {from: this.doc.sel.primary().head, to: null};
10069 if (margin == null) { margin = this.options.cursorScrollMargin; }
10070 } else if (typeof range$$1 == "number") {
10071 range$$1 = {from: Pos(range$$1, 0), to: null};
10072 } else if (range$$1.from == null) {
10073 range$$1 = {from: range$$1, to: null};
10074 }
10075 if (!range$$1.to) { range$$1.to = range$$1.from; }
10076 range$$1.margin = margin || 0;
10077
10078 if (range$$1.from.line != null) {
10079 scrollToRange(this, range$$1);
10080 } else {
10081 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
10082 }
10083 }),
10084
10085 setSize: methodOp(function(width, height) {
10086 var this$1 = this;
10087
10088 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
10089 if (width != null) { this.display.wrapper.style.width = interpret(width); }
10090 if (height != null) { this.display.wrapper.style.height = interpret(height); }
10091 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
10092 var lineNo$$1 = this.display.viewFrom;
10093 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
10094 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
10095 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
10096 ++lineNo$$1;
10097 });
10098 this.curOp.forceUpdate = true;
10099 signal(this, "refresh", this);
10100 }),
10101
10102 operation: function(f){return runInOp(this, f)},
10103 startOperation: function(){return startOperation(this)},
10104 endOperation: function(){return endOperation(this)},
10105
10106 refresh: methodOp(function() {
10107 var oldHeight = this.display.cachedTextHeight;
10108 regChange(this);
10109 this.curOp.forceUpdate = true;
10110 clearCaches(this);
10111 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10112 updateGutterSpace(this);
10113 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
10114 { estimateLineHeights(this); }
10115 signal(this, "refresh", this);
10116 }),
10117
10118 swapDoc: methodOp(function(doc) {
10119 var old = this.doc;
10120 old.cm = null;
10121 attachDoc(this, doc);
10122 clearCaches(this);
10123 this.display.input.reset();
10124 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10125 this.curOp.forceScroll = true;
10126 signalLater(this, "swapDoc", this, old);
10127 return old
10128 }),
10129
10130 getInputField: function(){return this.display.input.getField()},
10131 getWrapperElement: function(){return this.display.wrapper},
10132 getScrollerElement: function(){return this.display.scroller},
10133 getGutterElement: function(){return this.display.gutters}
10134 };
10135 eventMixin(CodeMirror);
10136
10137 CodeMirror.registerHelper = function(type, name, value) {
10138 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10139 helpers[type][name] = value;
10140 };
10141 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10142 CodeMirror.registerHelper(type, name, value);
10143 helpers[type]._global.push({pred: predicate, val: value});
10144 };
10145 };
10146
10147 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10148 // right), unit can be "char", "column" (like char, but doesn't
10149 // cross line boundaries), "word" (across next word), or "group" (to
10150 // the start of next group of word or non-word-non-whitespace
10151 // chars). The visually param controls whether, in right-to-left
10152 // text, direction 1 means to move towards the next index in the
10153 // string, or towards the character to the right of the current
10154 // position. The resulting position will have a hitSide=true
10155 // property if it reached the end of the document.
10156 function findPosH(doc, pos, dir, unit, visually) {
10157 var oldPos = pos;
10158 var origDir = dir;
10159 var lineObj = getLine(doc, pos.line);
10160 function findNextLine() {
10161 var l = pos.line + dir;
10162 if (l < doc.first || l >= doc.first + doc.size) { return false }
10163 pos = new Pos(l, pos.ch, pos.sticky);
10164 return lineObj = getLine(doc, l)
10165 }
10166 function moveOnce(boundToLine) {
10167 var next;
10168 if (visually) {
10169 next = moveVisually(doc.cm, lineObj, pos, dir);
10170 } else {
10171 next = moveLogically(lineObj, pos, dir);
10172 }
10173 if (next == null) {
10174 if (!boundToLine && findNextLine())
10175 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
10176 else
10177 { return false }
10178 } else {
10179 pos = next;
10180 }
10181 return true
10182 }
10183
10184 if (unit == "char") {
10185 moveOnce();
10186 } else if (unit == "column") {
10187 moveOnce(true);
10188 } else if (unit == "word" || unit == "group") {
10189 var sawType = null, group = unit == "group";
10190 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10191 for (var first = true;; first = false) {
10192 if (dir < 0 && !moveOnce(!first)) { break }
10193 var cur = lineObj.text.charAt(pos.ch) || "\n";
10194 var type = isWordChar(cur, helper) ? "w"
10195 : group && cur == "\n" ? "n"
10196 : !group || /\s/.test(cur) ? null
10197 : "p";
10198 if (group && !first && !type) { type = "s"; }
10199 if (sawType && sawType != type) {
10200 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10201 break
10202 }
10203
10204 if (type) { sawType = type; }
10205 if (dir > 0 && !moveOnce(!first)) { break }
10206 }
10207 }
10208 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10209 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10210 return result
10211 }
10212
10213 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10214 // "page" or "line". The resulting position will have a hitSide=true
10215 // property if it reached the end of the document.
10216 function findPosV(cm, pos, dir, unit) {
10217 var doc = cm.doc, x = pos.left, y;
10218 if (unit == "page") {
10219 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10220 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10221 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10222
10223 } else if (unit == "line") {
10224 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10225 }
10226 var target;
10227 for (;;) {
10228 target = coordsChar(cm, x, y);
10229 if (!target.outside) { break }
10230 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10231 y += dir * 5;
10232 }
10233 return target
10234 }
10235
10236 // CONTENTEDITABLE INPUT STYLE
10237
10238 var ContentEditableInput = function(cm) {
10239 this.cm = cm;
10240 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10241 this.polling = new Delayed();
10242 this.composing = null;
10243 this.gracePeriod = false;
10244 this.readDOMTimeout = null;
10245 };
10246
10247 ContentEditableInput.prototype.init = function (display) {
10248 var this$1 = this;
10249
10250 var input = this, cm = input.cm;
10251 var div = input.div = display.lineDiv;
10252 disableBrowserMagic(div, cm.options.spellcheck);
10253
10254 on(div, "paste", function (e) {
10255 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10256 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10257 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10258 });
10259
10260 on(div, "compositionstart", function (e) {
10261 this$1.composing = {data: e.data, done: false};
10262 });
10263 on(div, "compositionupdate", function (e) {
10264 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10265 });
10266 on(div, "compositionend", function (e) {
10267 if (this$1.composing) {
10268 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10269 this$1.composing.done = true;
10270 }
10271 });
10272
10273 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10274
10275 on(div, "input", function () {
10276 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10277 });
10278
10279 function onCopyCut(e) {
10280 if (signalDOMEvent(cm, e)) { return }
10281 if (cm.somethingSelected()) {
10282 setLastCopied({lineWise: false, text: cm.getSelections()});
10283 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10284 } else if (!cm.options.lineWiseCopyCut) {
10285 return
10286 } else {
10287 var ranges = copyableRanges(cm);
10288 setLastCopied({lineWise: true, text: ranges.text});
10289 if (e.type == "cut") {
10290 cm.operation(function () {
10291 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10292 cm.replaceSelection("", null, "cut");
10293 });
10294 }
10295 }
10296 if (e.clipboardData) {
10297 e.clipboardData.clearData();
10298 var content = lastCopied.text.join("\n");
10299 // iOS exposes the clipboard API, but seems to discard content inserted into it
10300 e.clipboardData.setData("Text", content);
10301 if (e.clipboardData.getData("Text") == content) {
10302 e.preventDefault();
10303 return
10304 }
10305 }
10306 // Old-fashioned briefly-focus-a-textarea hack
10307 var kludge = hiddenTextarea(), te = kludge.firstChild;
10308 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10309 te.value = lastCopied.text.join("\n");
10310 var hadFocus = document.activeElement;
10311 selectInput(te);
10312 setTimeout(function () {
10313 cm.display.lineSpace.removeChild(kludge);
10314 hadFocus.focus();
10315 if (hadFocus == div) { input.showPrimarySelection(); }
10316 }, 50);
10317 }
10318 on(div, "copy", onCopyCut);
10319 on(div, "cut", onCopyCut);
10320 };
10321
10322 ContentEditableInput.prototype.prepareSelection = function () {
10323 var result = prepareSelection(this.cm, false);
10324 result.focus = this.cm.state.focused;
10325 return result
10326 };
10327
10328 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10329 if (!info || !this.cm.display.view.length) { return }
10330 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10331 this.showMultipleSelections(info);
10332 };
10333
10334 ContentEditableInput.prototype.showPrimarySelection = function () {
10335 var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10336 var from = prim.from(), to = prim.to();
10337
10338 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10339 sel.removeAllRanges();
10340 return
10341 }
10342
10343 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10344 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10345 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10346 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10347 cmp(maxPos(curAnchor, curFocus), to) == 0)
10348 { return }
10349
10350 var view = cm.display.view;
10351 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10352 {node: view[0].measure.map[2], offset: 0};
10353 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10354 if (!end) {
10355 var measure = view[view.length - 1].measure;
10356 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10357 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10358 }
10359
10360 if (!start || !end) {
10361 sel.removeAllRanges();
10362 return
10363 }
10364
10365 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10366 try { rng = range(start.node, start.offset, end.offset, end.node); }
10367 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10368 if (rng) {
10369 if (!gecko && cm.state.focused) {
10370 sel.collapse(start.node, start.offset);
10371 if (!rng.collapsed) {
10372 sel.removeAllRanges();
10373 sel.addRange(rng);
10374 }
10375 } else {
10376 sel.removeAllRanges();
10377 sel.addRange(rng);
10378 }
10379 if (old && sel.anchorNode == null) { sel.addRange(old); }
10380 else if (gecko) { this.startGracePeriod(); }
10381 }
10382 this.rememberSelection();
10383 };
10384
10385 ContentEditableInput.prototype.startGracePeriod = function () {
10386 var this$1 = this;
10387
10388 clearTimeout(this.gracePeriod);
10389 this.gracePeriod = setTimeout(function () {
10390 this$1.gracePeriod = false;
10391 if (this$1.selectionChanged())
10392 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10393 }, 20);
10394 };
10395
10396 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10397 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10398 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10399 };
10400
10401 ContentEditableInput.prototype.rememberSelection = function () {
10402 var sel = window.getSelection();
10403 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10404 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10405 };
10406
10407 ContentEditableInput.prototype.selectionInEditor = function () {
10408 var sel = window.getSelection();
10409 if (!sel.rangeCount) { return false }
10410 var node = sel.getRangeAt(0).commonAncestorContainer;
10411 return contains(this.div, node)
10412 };
10413
10414 ContentEditableInput.prototype.focus = function () {
10415 if (this.cm.options.readOnly != "nocursor") {
10416 if (!this.selectionInEditor())
10417 { this.showSelection(this.prepareSelection(), true); }
10418 this.div.focus();
10419 }
10420 };
10421 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10422 ContentEditableInput.prototype.getField = function () { return this.div };
10423
10424 ContentEditableInput.prototype.supportsTouch = function () { return true };
10425
10426 ContentEditableInput.prototype.receivedFocus = function () {
10427 var input = this;
10428 if (this.selectionInEditor())
10429 { this.pollSelection(); }
10430 else
10431 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10432
10433 function poll() {
10434 if (input.cm.state.focused) {
10435 input.pollSelection();
10436 input.polling.set(input.cm.options.pollInterval, poll);
10437 }
10438 }
10439 this.polling.set(this.cm.options.pollInterval, poll);
10440 };
10441
10442 ContentEditableInput.prototype.selectionChanged = function () {
10443 var sel = window.getSelection();
10444 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10445 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10446 };
10447
10448 ContentEditableInput.prototype.pollSelection = function () {
10449 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10450 var sel = window.getSelection(), cm = this.cm;
10451 // On Android Chrome (version 56, at least), backspacing into an
10452 // uneditable block element will put the cursor in that element,
10453 // and then, because it's not editable, hide the virtual keyboard.
10454 // Because Android doesn't allow us to actually detect backspace
10455 // presses in a sane way, this code checks for when that happens
10456 // and simulates a backspace press in this case.
10457 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
10458 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10459 this.blur();
10460 this.focus();
10461 return
10462 }
10463 if (this.composing) { return }
10464 this.rememberSelection();
10465 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10466 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10467 if (anchor && head) { runInOp(cm, function () {
10468 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10469 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10470 }); }
10471 };
10472
10473 ContentEditableInput.prototype.pollContent = function () {
10474 if (this.readDOMTimeout != null) {
10475 clearTimeout(this.readDOMTimeout);
10476 this.readDOMTimeout = null;
10477 }
10478
10479 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10480 var from = sel.from(), to = sel.to();
10481 if (from.ch == 0 && from.line > cm.firstLine())
10482 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10483 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10484 { to = Pos(to.line + 1, 0); }
10485 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10486
10487 var fromIndex, fromLine, fromNode;
10488 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10489 fromLine = lineNo(display.view[0].line);
10490 fromNode = display.view[0].node;
10491 } else {
10492 fromLine = lineNo(display.view[fromIndex].line);
10493 fromNode = display.view[fromIndex - 1].node.nextSibling;
10494 }
10495 var toIndex = findViewIndex(cm, to.line);
10496 var toLine, toNode;
10497 if (toIndex == display.view.length - 1) {
10498 toLine = display.viewTo - 1;
10499 toNode = display.lineDiv.lastChild;
10500 } else {
10501 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10502 toNode = display.view[toIndex + 1].node.previousSibling;
10503 }
10504
10505 if (!fromNode) { return false }
10506 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10507 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10508 while (newText.length > 1 && oldText.length > 1) {
10509 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10510 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10511 else { break }
10512 }
10513
10514 var cutFront = 0, cutEnd = 0;
10515 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10516 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10517 { ++cutFront; }
10518 var newBot = lst(newText), oldBot = lst(oldText);
10519 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10520 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10521 while (cutEnd < maxCutEnd &&
10522 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10523 { ++cutEnd; }
10524 // Try to move start of change to start of selection if ambiguous
10525 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10526 while (cutFront && cutFront > from.ch &&
10527 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10528 cutFront--;
10529 cutEnd++;
10530 }
10531 }
10532
10533 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10534 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10535
10536 var chFrom = Pos(fromLine, cutFront);
10537 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10538 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10539 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10540 return true
10541 }
10542 };
10543
10544 ContentEditableInput.prototype.ensurePolled = function () {
10545 this.forceCompositionEnd();
10546 };
10547 ContentEditableInput.prototype.reset = function () {
10548 this.forceCompositionEnd();
10549 };
10550 ContentEditableInput.prototype.forceCompositionEnd = function () {
10551 if (!this.composing) { return }
10552 clearTimeout(this.readDOMTimeout);
10553 this.composing = null;
10554 this.updateFromDOM();
10555 this.div.blur();
10556 this.div.focus();
10557 };
10558 ContentEditableInput.prototype.readFromDOMSoon = function () {
10559 var this$1 = this;
10560
10561 if (this.readDOMTimeout != null) { return }
10562 this.readDOMTimeout = setTimeout(function () {
10563 this$1.readDOMTimeout = null;
10564 if (this$1.composing) {
10565 if (this$1.composing.done) { this$1.composing = null; }
10566 else { return }
10567 }
10568 this$1.updateFromDOM();
10569 }, 80);
10570 };
10571
10572 ContentEditableInput.prototype.updateFromDOM = function () {
10573 var this$1 = this;
10574
10575 if (this.cm.isReadOnly() || !this.pollContent())
10576 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10577 };
10578
10579 ContentEditableInput.prototype.setUneditable = function (node) {
10580 node.contentEditable = "false";
10581 };
10582
10583 ContentEditableInput.prototype.onKeyPress = function (e) {
10584 if (e.charCode == 0) { return }
10585 e.preventDefault();
10586 if (!this.cm.isReadOnly())
10587 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10588 };
10589
10590 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10591 this.div.contentEditable = String(val != "nocursor");
10592 };
10593
10594 ContentEditableInput.prototype.onContextMenu = function () {};
10595 ContentEditableInput.prototype.resetPosition = function () {};
10596
10597 ContentEditableInput.prototype.needsContentAttribute = true;
10598
10599 function posToDOM(cm, pos) {
10600 var view = findViewForLine(cm, pos.line);
10601 if (!view || view.hidden) { return null }
10602 var line = getLine(cm.doc, pos.line);
10603 var info = mapFromLineView(view, line, pos.line);
10604
10605 var order = getOrder(line, cm.doc.direction), side = "left";
10606 if (order) {
10607 var partPos = getBidiPartAt(order, pos.ch);
10608 side = partPos % 2 ? "right" : "left";
10609 }
10610 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10611 result.offset = result.collapse == "right" ? result.end : result.start;
10612 return result
10613 }
10614
10615 function isInGutter(node) {
10616 for (var scan = node; scan; scan = scan.parentNode)
10617 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10618 return false
10619 }
10620
10621 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10622
10623 function domTextBetween(cm, from, to, fromLine, toLine) {
10624 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
10625 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10626 function close() {
10627 if (closing) {
10628 text += lineSep;
10629 closing = false;
10630 }
10631 }
10632 function addText(str) {
10633 if (str) {
10634 close();
10635 text += str;
10636 }
10637 }
10638 function walk(node) {
10639 if (node.nodeType == 1) {
10640 var cmText = node.getAttribute("cm-text");
10641 if (cmText != null) {
10642 addText(cmText || node.textContent.replace(/\u200b/g, ""));
10643 return
10644 }
10645 var markerID = node.getAttribute("cm-marker"), range$$1;
10646 if (markerID) {
10647 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10648 if (found.length && (range$$1 = found[0].find(0)))
10649 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10650 return
10651 }
10652 if (node.getAttribute("contenteditable") == "false") { return }
10653 var isBlock = /^(pre|div|p)$/i.test(node.nodeName);
10654 if (isBlock) { close(); }
10655 for (var i = 0; i < node.childNodes.length; i++)
10656 { walk(node.childNodes[i]); }
10657 if (isBlock) { closing = true; }
10658 } else if (node.nodeType == 3) {
10659 addText(node.nodeValue);
10660 }
10661 }
10662 for (;;) {
10663 walk(from);
10664 if (from == to) { break }
10665 from = from.nextSibling;
10666 }
10667 return text
10668 }
10669
10670 function domToPos(cm, node, offset) {
10671 var lineNode;
10672 if (node == cm.display.lineDiv) {
10673 lineNode = cm.display.lineDiv.childNodes[offset];
10674 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10675 node = null; offset = 0;
10676 } else {
10677 for (lineNode = node;; lineNode = lineNode.parentNode) {
10678 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10679 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10680 }
10681 }
10682 for (var i = 0; i < cm.display.view.length; i++) {
10683 var lineView = cm.display.view[i];
10684 if (lineView.node == lineNode)
10685 { return locateNodeInLineView(lineView, node, offset) }
10686 }
10687 }
10688
10689 function locateNodeInLineView(lineView, node, offset) {
10690 var wrapper = lineView.text.firstChild, bad = false;
10691 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10692 if (node == wrapper) {
10693 bad = true;
10694 node = wrapper.childNodes[offset];
10695 offset = 0;
10696 if (!node) {
10697 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10698 return badPos(Pos(lineNo(line), line.text.length), bad)
10699 }
10700 }
10701
10702 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10703 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10704 textNode = node.firstChild;
10705 if (offset) { offset = textNode.nodeValue.length; }
10706 }
10707 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10708 var measure = lineView.measure, maps = measure.maps;
10709
10710 function find(textNode, topNode, offset) {
10711 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10712 var map$$1 = i < 0 ? measure.map : maps[i];
10713 for (var j = 0; j < map$$1.length; j += 3) {
10714 var curNode = map$$1[j + 2];
10715 if (curNode == textNode || curNode == topNode) {
10716 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10717 var ch = map$$1[j] + offset;
10718 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10719 return Pos(line, ch)
10720 }
10721 }
10722 }
10723 }
10724 var found = find(textNode, topNode, offset);
10725 if (found) { return badPos(found, bad) }
10726
10727 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10728 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10729 found = find(after, after.firstChild, 0);
10730 if (found)
10731 { return badPos(Pos(found.line, found.ch - dist), bad) }
10732 else
10733 { dist += after.textContent.length; }
10734 }
10735 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10736 found = find(before, before.firstChild, -1);
10737 if (found)
10738 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10739 else
10740 { dist$1 += before.textContent.length; }
10741 }
10742 }
10743
10744 // TEXTAREA INPUT STYLE
10745
10746 var TextareaInput = function(cm) {
10747 this.cm = cm;
10748 // See input.poll and input.reset
10749 this.prevInput = "";
10750
10751 // Flag that indicates whether we expect input to appear real soon
10752 // now (after some event like 'keypress' or 'input') and are
10753 // polling intensively.
10754 this.pollingFast = false;
10755 // Self-resetting timeout for the poller
10756 this.polling = new Delayed();
10757 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10758 this.hasSelection = false;
10759 this.composing = null;
10760 };
10761
10762 TextareaInput.prototype.init = function (display) {
10763 var this$1 = this;
10764
10765 var input = this, cm = this.cm;
10766
10767 // Wraps and hides input textarea
10768 var div = this.wrapper = hiddenTextarea();
10769 // The semihidden textarea that is focused when the editor is
10770 // focused, and receives input.
10771 var te = this.textarea = div.firstChild;
10772 display.wrapper.insertBefore(div, display.wrapper.firstChild);
10773
10774 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10775 if (ios) { te.style.width = "0px"; }
10776
10777 on(te, "input", function () {
10778 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10779 input.poll();
10780 });
10781
10782 on(te, "paste", function (e) {
10783 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10784
10785 cm.state.pasteIncoming = true;
10786 input.fastPoll();
10787 });
10788
10789 function prepareCopyCut(e) {
10790 if (signalDOMEvent(cm, e)) { return }
10791 if (cm.somethingSelected()) {
10792 setLastCopied({lineWise: false, text: cm.getSelections()});
10793 } else if (!cm.options.lineWiseCopyCut) {
10794 return
10795 } else {
10796 var ranges = copyableRanges(cm);
10797 setLastCopied({lineWise: true, text: ranges.text});
10798 if (e.type == "cut") {
10799 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10800 } else {
10801 input.prevInput = "";
10802 te.value = ranges.text.join("\n");
10803 selectInput(te);
10804 }
10805 }
10806 if (e.type == "cut") { cm.state.cutIncoming = true; }
10807 }
10808 on(te, "cut", prepareCopyCut);
10809 on(te, "copy", prepareCopyCut);
10810
10811 on(display.scroller, "paste", function (e) {
10812 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10813 cm.state.pasteIncoming = true;
10814 input.focus();
10815 });
10816
10817 // Prevent normal selection in the editor (we handle our own)
10818 on(display.lineSpace, "selectstart", function (e) {
10819 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10820 });
10821
10822 on(te, "compositionstart", function () {
10823 var start = cm.getCursor("from");
10824 if (input.composing) { input.composing.range.clear(); }
10825 input.composing = {
10826 start: start,
10827 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10828 };
10829 });
10830 on(te, "compositionend", function () {
10831 if (input.composing) {
10832 input.poll();
10833 input.composing.range.clear();
10834 input.composing = null;
10835 }
10836 });
10837 };
10838
10839 TextareaInput.prototype.prepareSelection = function () {
10840 // Redraw the selection and/or cursor
10841 var cm = this.cm, display = cm.display, doc = cm.doc;
10842 var result = prepareSelection(cm);
10843
10844 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10845 if (cm.options.moveInputWithCursor) {
10846 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10847 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10848 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10849 headPos.top + lineOff.top - wrapOff.top));
10850 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10851 headPos.left + lineOff.left - wrapOff.left));
10852 }
10853
10854 return result
10855 };
10856
10857 TextareaInput.prototype.showSelection = function (drawn) {
10858 var cm = this.cm, display = cm.display;
10859 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10860 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10861 if (drawn.teTop != null) {
10862 this.wrapper.style.top = drawn.teTop + "px";
10863 this.wrapper.style.left = drawn.teLeft + "px";
10864 }
10865 };
10866
10867 // Reset the input to correspond to the selection (or to be empty,
10868 // when not typing and nothing is selected)
10869 TextareaInput.prototype.reset = function (typing) {
10870 if (this.contextMenuPending || this.composing) { return }
10871 var cm = this.cm;
10872 if (cm.somethingSelected()) {
10873 this.prevInput = "";
10874 var content = cm.getSelection();
10875 this.textarea.value = content;
10876 if (cm.state.focused) { selectInput(this.textarea); }
10877 if (ie && ie_version >= 9) { this.hasSelection = content; }
10878 } else if (!typing) {
10879 this.prevInput = this.textarea.value = "";
10880 if (ie && ie_version >= 9) { this.hasSelection = null; }
10881 }
10882 };
10883
10884 TextareaInput.prototype.getField = function () { return this.textarea };
10885
10886 TextareaInput.prototype.supportsTouch = function () { return false };
10887
10888 TextareaInput.prototype.focus = function () {
10889 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10890 try { this.textarea.focus(); }
10891 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
10892 }
10893 };
10894
10895 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
10896
10897 TextareaInput.prototype.resetPosition = function () {
10898 this.wrapper.style.top = this.wrapper.style.left = 0;
10899 };
10900
10901 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
10902
10903 // Poll for input changes, using the normal rate of polling. This
10904 // runs as long as the editor is focused.
10905 TextareaInput.prototype.slowPoll = function () {
10906 var this$1 = this;
10907
10908 if (this.pollingFast) { return }
10909 this.polling.set(this.cm.options.pollInterval, function () {
10910 this$1.poll();
10911 if (this$1.cm.state.focused) { this$1.slowPoll(); }
10912 });
10913 };
10914
10915 // When an event has just come in that is likely to add or change
10916 // something in the input textarea, we poll faster, to ensure that
10917 // the change appears on the screen quickly.
10918 TextareaInput.prototype.fastPoll = function () {
10919 var missed = false, input = this;
10920 input.pollingFast = true;
10921 function p() {
10922 var changed = input.poll();
10923 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
10924 else {input.pollingFast = false; input.slowPoll();}
10925 }
10926 input.polling.set(20, p);
10927 };
10928
10929 // Read input from the textarea, and update the document to match.
10930 // When something is selected, it is present in the textarea, and
10931 // selected (unless it is huge, in which case a placeholder is
10932 // used). When nothing is selected, the cursor sits after previously
10933 // seen text (can be empty), which is stored in prevInput (we must
10934 // not reset the textarea when typing, because that breaks IME).
10935 TextareaInput.prototype.poll = function () {
10936 var this$1 = this;
10937
10938 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
10939 // Since this is called a *lot*, try to bail out as cheaply as
10940 // possible when it is clear that nothing happened. hasSelection
10941 // will be the case when there is a lot of text in the textarea,
10942 // in which case reading its value would be expensive.
10943 if (this.contextMenuPending || !cm.state.focused ||
10944 (hasSelection(input) && !prevInput && !this.composing) ||
10945 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
10946 { return false }
10947
10948 var text = input.value;
10949 // If nothing changed, bail.
10950 if (text == prevInput && !cm.somethingSelected()) { return false }
10951 // Work around nonsensical selection resetting in IE9/10, and
10952 // inexplicable appearance of private area unicode characters on
10953 // some key combos in Mac (#2689).
10954 if (ie && ie_version >= 9 && this.hasSelection === text ||
10955 mac && /[\uf700-\uf7ff]/.test(text)) {
10956 cm.display.input.reset();
10957 return false
10958 }
10959
10960 if (cm.doc.sel == cm.display.selForContextMenu) {
10961 var first = text.charCodeAt(0);
10962 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
10963 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
10964 }
10965 // Find the part of the input that is actually new
10966 var same = 0, l = Math.min(prevInput.length, text.length);
10967 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
10968
10969 runInOp(cm, function () {
10970 applyTextInput(cm, text.slice(same), prevInput.length - same,
10971 null, this$1.composing ? "*compose" : null);
10972
10973 // Don't leave long text in the textarea, since it makes further polling slow
10974 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
10975 else { this$1.prevInput = text; }
10976
10977 if (this$1.composing) {
10978 this$1.composing.range.clear();
10979 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
10980 {className: "CodeMirror-composing"});
10981 }
10982 });
10983 return true
10984 };
10985
10986 TextareaInput.prototype.ensurePolled = function () {
10987 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
10988 };
10989
10990 TextareaInput.prototype.onKeyPress = function () {
10991 if (ie && ie_version >= 9) { this.hasSelection = null; }
10992 this.fastPoll();
10993 };
10994
10995 TextareaInput.prototype.onContextMenu = function (e) {
10996 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
10997 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
10998 if (!pos || presto) { return } // Opera is difficult.
10999
11000 // Reset the current text selection only if the click is done outside of the selection
11001 // and 'resetSelectionOnContextMenu' option is true.
11002 var reset = cm.options.resetSelectionOnContextMenu;
11003 if (reset && cm.doc.sel.contains(pos) == -1)
11004 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
11005
11006 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
11007 input.wrapper.style.cssText = "position: absolute";
11008 var wrapperBox = input.wrapper.getBoundingClientRect();
11009 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);";
11010 var oldScrollY;
11011 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
11012 display.input.focus();
11013 if (webkit) { window.scrollTo(null, oldScrollY); }
11014 display.input.reset();
11015 // Adds "Select all" to context menu in FF
11016 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
11017 input.contextMenuPending = true;
11018 display.selForContextMenu = cm.doc.sel;
11019 clearTimeout(display.detectingSelectAll);
11020
11021 // Select-all will be greyed out if there's nothing to select, so
11022 // this adds a zero-width space so that we can later check whether
11023 // it got selected.
11024 function prepareSelectAllHack() {
11025 if (te.selectionStart != null) {
11026 var selected = cm.somethingSelected();
11027 var extval = "\u200b" + (selected ? te.value : "");
11028 te.value = "\u21da"; // Used to catch context-menu undo
11029 te.value = extval;
11030 input.prevInput = selected ? "" : "\u200b";
11031 te.selectionStart = 1; te.selectionEnd = extval.length;
11032 // Re-set this, in case some other handler touched the
11033 // selection in the meantime.
11034 display.selForContextMenu = cm.doc.sel;
11035 }
11036 }
11037 function rehide() {
11038 input.contextMenuPending = false;
11039 input.wrapper.style.cssText = oldWrapperCSS;
11040 te.style.cssText = oldCSS;
11041 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
11042
11043 // Try to detect the user choosing select-all
11044 if (te.selectionStart != null) {
11045 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
11046 var i = 0, poll = function () {
11047 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
11048 te.selectionEnd > 0 && input.prevInput == "\u200b") {
11049 operation(cm, selectAll)(cm);
11050 } else if (i++ < 10) {
11051 display.detectingSelectAll = setTimeout(poll, 500);
11052 } else {
11053 display.selForContextMenu = null;
11054 display.input.reset();
11055 }
11056 };
11057 display.detectingSelectAll = setTimeout(poll, 200);
11058 }
11059 }
11060
11061 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
11062 if (captureRightClick) {
11063 e_stop(e);
11064 var mouseup = function () {
11065 off(window, "mouseup", mouseup);
11066 setTimeout(rehide, 20);
11067 };
11068 on(window, "mouseup", mouseup);
11069 } else {
11070 setTimeout(rehide, 50);
11071 }
11072 };
11073
11074 TextareaInput.prototype.readOnlyChanged = function (val) {
11075 if (!val) { this.reset(); }
11076 this.textarea.disabled = val == "nocursor";
11077 };
11078
11079 TextareaInput.prototype.setUneditable = function () {};
11080
11081 TextareaInput.prototype.needsContentAttribute = false;
11082
11083 function fromTextArea(textarea, options) {
11084 options = options ? copyObj(options) : {};
11085 options.value = textarea.value;
11086 if (!options.tabindex && textarea.tabIndex)
11087 { options.tabindex = textarea.tabIndex; }
11088 if (!options.placeholder && textarea.placeholder)
11089 { options.placeholder = textarea.placeholder; }
11090 // Set autofocus to true if this textarea is focused, or if it has
11091 // autofocus and no other element is focused.
11092 if (options.autofocus == null) {
11093 var hasFocus = activeElt();
11094 options.autofocus = hasFocus == textarea ||
11095 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
11096 }
11097
11098 function save() {textarea.value = cm.getValue();}
11099
11100 var realSubmit;
11101 if (textarea.form) {
11102 on(textarea.form, "submit", save);
11103 // Deplorable hack to make the submit method do the right thing.
11104 if (!options.leaveSubmitMethodAlone) {
11105 var form = textarea.form;
11106 realSubmit = form.submit;
11107 try {
11108 var wrappedSubmit = form.submit = function () {
11109 save();
11110 form.submit = realSubmit;
11111 form.submit();
11112 form.submit = wrappedSubmit;
11113 };
11114 } catch(e) {}
11115 }
11116 }
11117
11118 options.finishInit = function (cm) {
11119 cm.save = save;
11120 cm.getTextArea = function () { return textarea; };
11121 cm.toTextArea = function () {
11122 cm.toTextArea = isNaN; // Prevent this from being ran twice
11123 save();
11124 textarea.parentNode.removeChild(cm.getWrapperElement());
11125 textarea.style.display = "";
11126 if (textarea.form) {
11127 off(textarea.form, "submit", save);
11128 if (typeof textarea.form.submit == "function")
11129 { textarea.form.submit = realSubmit; }
11130 }
11131 };
11132 };
11133
11134 textarea.style.display = "none";
11135 var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11136 options);
11137 return cm
11138 }
11139
11140 function addLegacyProps(CodeMirror) {
11141 CodeMirror.off = off;
11142 CodeMirror.on = on;
11143 CodeMirror.wheelEventPixels = wheelEventPixels;
11144 CodeMirror.Doc = Doc;
11145 CodeMirror.splitLines = splitLinesAuto;
11146 CodeMirror.countColumn = countColumn;
11147 CodeMirror.findColumn = findColumn;
11148 CodeMirror.isWordChar = isWordCharBasic;
11149 CodeMirror.Pass = Pass;
11150 CodeMirror.signal = signal;
11151 CodeMirror.Line = Line;
11152 CodeMirror.changeEnd = changeEnd;
11153 CodeMirror.scrollbarModel = scrollbarModel;
11154 CodeMirror.Pos = Pos;
11155 CodeMirror.cmpPos = cmp;
11156 CodeMirror.modes = modes;
11157 CodeMirror.mimeModes = mimeModes;
11158 CodeMirror.resolveMode = resolveMode;
11159 CodeMirror.getMode = getMode;
11160 CodeMirror.modeExtensions = modeExtensions;
11161 CodeMirror.extendMode = extendMode;
11162 CodeMirror.copyState = copyState;
11163 CodeMirror.startState = startState;
11164 CodeMirror.innerMode = innerMode;
11165 CodeMirror.commands = commands;
11166 CodeMirror.keyMap = keyMap;
11167 CodeMirror.keyName = keyName;
11168 CodeMirror.isModifierKey = isModifierKey;
11169 CodeMirror.lookupKey = lookupKey;
11170 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11171 CodeMirror.StringStream = StringStream;
11172 CodeMirror.SharedTextMarker = SharedTextMarker;
11173 CodeMirror.TextMarker = TextMarker;
11174 CodeMirror.LineWidget = LineWidget;
11175 CodeMirror.e_preventDefault = e_preventDefault;
11176 CodeMirror.e_stopPropagation = e_stopPropagation;
11177 CodeMirror.e_stop = e_stop;
11178 CodeMirror.addClass = addClass;
11179 CodeMirror.contains = contains;
11180 CodeMirror.rmClass = rmClass;
11181 CodeMirror.keyNames = keyNames;
11182 }
11183
11184 // EDITOR CONSTRUCTOR
11185
11186 defineOptions(CodeMirror$1);
11187
11188 addEditorMethods(CodeMirror$1);
11189
11190 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11191 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11192 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11193 { CodeMirror$1.prototype[prop] = (function(method) {
11194 return function() {return method.apply(this.doc, arguments)}
11195 })(Doc.prototype[prop]); } }
11196
11197 eventMixin(Doc);
11198
11199 // INPUT HANDLING
11200
11201 CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11202
11203 // MODE DEFINITION AND QUERYING
11204
11205 // Extra arguments are stored as the mode's dependencies, which is
11206 // used by (legacy) mechanisms like loadmode.js to automatically
11207 // load a mode. (Preferred mechanism is the require/define calls.)
11208 CodeMirror$1.defineMode = function(name/*, mode, …*/) {
11209 if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; }
11210 defineMode.apply(this, arguments);
11211 };
11212
11213 CodeMirror$1.defineMIME = defineMIME;
11214
11215 // Minimal default mode.
11216 CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11217 CodeMirror$1.defineMIME("text/plain", "null");
11218
11219 // EXTENSIONS
11220
11221 CodeMirror$1.defineExtension = function (name, func) {
11222 CodeMirror$1.prototype[name] = func;
11223 };
11224 CodeMirror$1.defineDocExtension = function (name, func) {
11225 Doc.prototype[name] = func;
11226 };
11227
11228 CodeMirror$1.fromTextArea = fromTextArea;
11229
11230 addLegacyProps(CodeMirror$1);
11231
11232 CodeMirror$1.version = "5.34.0";
11233
11234 return CodeMirror$1;
11235
11236 })));
11237
11238 },{}],15:[function(require,module,exports){
11239 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11240 // Distributed under an MIT license: http://codemirror.net/LICENSE
11241
11242 (function(mod) {
11243 if (typeof exports == "object" && typeof module == "object") // CommonJS
11244 mod(require("../../lib/codemirror"));
11245 else if (typeof define == "function" && define.amd) // AMD
11246 define(["../../lib/codemirror"], mod);
11247 else // Plain browser env
11248 mod(CodeMirror);
11249 })(function(CodeMirror) {
11250 "use strict";
11251
11252 CodeMirror.defineMode("css", function(config, parserConfig) {
11253 var inline = parserConfig.inline
11254 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11255
11256 var indentUnit = config.indentUnit,
11257 tokenHooks = parserConfig.tokenHooks,
11258 documentTypes = parserConfig.documentTypes || {},
11259 mediaTypes = parserConfig.mediaTypes || {},
11260 mediaFeatures = parserConfig.mediaFeatures || {},
11261 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11262 propertyKeywords = parserConfig.propertyKeywords || {},
11263 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11264 fontProperties = parserConfig.fontProperties || {},
11265 counterDescriptors = parserConfig.counterDescriptors || {},
11266 colorKeywords = parserConfig.colorKeywords || {},
11267 valueKeywords = parserConfig.valueKeywords || {},
11268 allowNested = parserConfig.allowNested,
11269 lineComment = parserConfig.lineComment,
11270 supportsAtComponent = parserConfig.supportsAtComponent === true;
11271
11272 var type, override;
11273 function ret(style, tp) { type = tp; return style; }
11274
11275 // Tokenizers
11276
11277 function tokenBase(stream, state) {
11278 var ch = stream.next();
11279 if (tokenHooks[ch]) {
11280 var result = tokenHooks[ch](stream, state);
11281 if (result !== false) return result;
11282 }
11283 if (ch == "@") {
11284 stream.eatWhile(/[\w\\\-]/);
11285 return ret("def", stream.current());
11286 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11287 return ret(null, "compare");
11288 } else if (ch == "\"" || ch == "'") {
11289 state.tokenize = tokenString(ch);
11290 return state.tokenize(stream, state);
11291 } else if (ch == "#") {
11292 stream.eatWhile(/[\w\\\-]/);
11293 return ret("atom", "hash");
11294 } else if (ch == "!") {
11295 stream.match(/^\s*\w*/);
11296 return ret("keyword", "important");
11297 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11298 stream.eatWhile(/[\w.%]/);
11299 return ret("number", "unit");
11300 } else if (ch === "-") {
11301 if (/[\d.]/.test(stream.peek())) {
11302 stream.eatWhile(/[\w.%]/);
11303 return ret("number", "unit");
11304 } else if (stream.match(/^-[\w\\\-]+/)) {
11305 stream.eatWhile(/[\w\\\-]/);
11306 if (stream.match(/^\s*:/, false))
11307 return ret("variable-2", "variable-definition");
11308 return ret("variable-2", "variable");
11309 } else if (stream.match(/^\w+-/)) {
11310 return ret("meta", "meta");
11311 }
11312 } else if (/[,+>*\/]/.test(ch)) {
11313 return ret(null, "select-op");
11314 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11315 return ret("qualifier", "qualifier");
11316 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11317 return ret(null, ch);
11318 } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
11319 ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
11320 ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
11321 stream.backUp(1);
11322 state.tokenize = tokenParenthesized;
11323 return ret("property", "word");
11324 } else if (/[\w\\\-]/.test(ch)) {
11325 stream.eatWhile(/[\w\\\-]/);
11326 return ret("property", "word");
11327 } else {
11328 return ret(null, null);
11329 }
11330 }
11331
11332 function tokenString(quote) {
11333 return function(stream, state) {
11334 var escaped = false, ch;
11335 while ((ch = stream.next()) != null) {
11336 if (ch == quote && !escaped) {
11337 if (quote == ")") stream.backUp(1);
11338 break;
11339 }
11340 escaped = !escaped && ch == "\\";
11341 }
11342 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11343 return ret("string", "string");
11344 };
11345 }
11346
11347 function tokenParenthesized(stream, state) {
11348 stream.next(); // Must be '('
11349 if (!stream.match(/\s*[\"\')]/, false))
11350 state.tokenize = tokenString(")");
11351 else
11352 state.tokenize = null;
11353 return ret(null, "(");
11354 }
11355
11356 // Context management
11357
11358 function Context(type, indent, prev) {
11359 this.type = type;
11360 this.indent = indent;
11361 this.prev = prev;
11362 }
11363
11364 function pushContext(state, stream, type, indent) {
11365 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11366 return type;
11367 }
11368
11369 function popContext(state) {
11370 if (state.context.prev)
11371 state.context = state.context.prev;
11372 return state.context.type;
11373 }
11374
11375 function pass(type, stream, state) {
11376 return states[state.context.type](type, stream, state);
11377 }
11378 function popAndPass(type, stream, state, n) {
11379 for (var i = n || 1; i > 0; i--)
11380 state.context = state.context.prev;
11381 return pass(type, stream, state);
11382 }
11383
11384 // Parser
11385
11386 function wordAsValue(stream) {
11387 var word = stream.current().toLowerCase();
11388 if (valueKeywords.hasOwnProperty(word))
11389 override = "atom";
11390 else if (colorKeywords.hasOwnProperty(word))
11391 override = "keyword";
11392 else
11393 override = "variable";
11394 }
11395
11396 var states = {};
11397
11398 states.top = function(type, stream, state) {
11399 if (type == "{") {
11400 return pushContext(state, stream, "block");
11401 } else if (type == "}" && state.context.prev) {
11402 return popContext(state);
11403 } else if (supportsAtComponent && /@component/i.test(type)) {
11404 return pushContext(state, stream, "atComponentBlock");
11405 } else if (/^@(-moz-)?document$/i.test(type)) {
11406 return pushContext(state, stream, "documentTypes");
11407 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11408 return pushContext(state, stream, "atBlock");
11409 } else if (/^@(font-face|counter-style)/i.test(type)) {
11410 state.stateArg = type;
11411 return "restricted_atBlock_before";
11412 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11413 return "keyframes";
11414 } else if (type && type.charAt(0) == "@") {
11415 return pushContext(state, stream, "at");
11416 } else if (type == "hash") {
11417 override = "builtin";
11418 } else if (type == "word") {
11419 override = "tag";
11420 } else if (type == "variable-definition") {
11421 return "maybeprop";
11422 } else if (type == "interpolation") {
11423 return pushContext(state, stream, "interpolation");
11424 } else if (type == ":") {
11425 return "pseudo";
11426 } else if (allowNested && type == "(") {
11427 return pushContext(state, stream, "parens");
11428 }
11429 return state.context.type;
11430 };
11431
11432 states.block = function(type, stream, state) {
11433 if (type == "word") {
11434 var word = stream.current().toLowerCase();
11435 if (propertyKeywords.hasOwnProperty(word)) {
11436 override = "property";
11437 return "maybeprop";
11438 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11439 override = "string-2";
11440 return "maybeprop";
11441 } else if (allowNested) {
11442 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11443 return "block";
11444 } else {
11445 override += " error";
11446 return "maybeprop";
11447 }
11448 } else if (type == "meta") {
11449 return "block";
11450 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11451 override = "error";
11452 return "block";
11453 } else {
11454 return states.top(type, stream, state);
11455 }
11456 };
11457
11458 states.maybeprop = function(type, stream, state) {
11459 if (type == ":") return pushContext(state, stream, "prop");
11460 return pass(type, stream, state);
11461 };
11462
11463 states.prop = function(type, stream, state) {
11464 if (type == ";") return popContext(state);
11465 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11466 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11467 if (type == "(") return pushContext(state, stream, "parens");
11468
11469 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11470 override += " error";
11471 } else if (type == "word") {
11472 wordAsValue(stream);
11473 } else if (type == "interpolation") {
11474 return pushContext(state, stream, "interpolation");
11475 }
11476 return "prop";
11477 };
11478
11479 states.propBlock = function(type, _stream, state) {
11480 if (type == "}") return popContext(state);
11481 if (type == "word") { override = "property"; return "maybeprop"; }
11482 return state.context.type;
11483 };
11484
11485 states.parens = function(type, stream, state) {
11486 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11487 if (type == ")") return popContext(state);
11488 if (type == "(") return pushContext(state, stream, "parens");
11489 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11490 if (type == "word") wordAsValue(stream);
11491 return "parens";
11492 };
11493
11494 states.pseudo = function(type, stream, state) {
11495 if (type == "meta") return "pseudo";
11496
11497 if (type == "word") {
11498 override = "variable-3";
11499 return state.context.type;
11500 }
11501 return pass(type, stream, state);
11502 };
11503
11504 states.documentTypes = function(type, stream, state) {
11505 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11506 override = "tag";
11507 return state.context.type;
11508 } else {
11509 return states.atBlock(type, stream, state);
11510 }
11511 };
11512
11513 states.atBlock = function(type, stream, state) {
11514 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11515 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11516 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11517
11518 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11519
11520 if (type == "word") {
11521 var word = stream.current().toLowerCase();
11522 if (word == "only" || word == "not" || word == "and" || word == "or")
11523 override = "keyword";
11524 else if (mediaTypes.hasOwnProperty(word))
11525 override = "attribute";
11526 else if (mediaFeatures.hasOwnProperty(word))
11527 override = "property";
11528 else if (mediaValueKeywords.hasOwnProperty(word))
11529 override = "keyword";
11530 else if (propertyKeywords.hasOwnProperty(word))
11531 override = "property";
11532 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11533 override = "string-2";
11534 else if (valueKeywords.hasOwnProperty(word))
11535 override = "atom";
11536 else if (colorKeywords.hasOwnProperty(word))
11537 override = "keyword";
11538 else
11539 override = "error";
11540 }
11541 return state.context.type;
11542 };
11543
11544 states.atComponentBlock = function(type, stream, state) {
11545 if (type == "}")
11546 return popAndPass(type, stream, state);
11547 if (type == "{")
11548 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11549 if (type == "word")
11550 override = "error";
11551 return state.context.type;
11552 };
11553
11554 states.atBlock_parens = function(type, stream, state) {
11555 if (type == ")") return popContext(state);
11556 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11557 return states.atBlock(type, stream, state);
11558 };
11559
11560 states.restricted_atBlock_before = function(type, stream, state) {
11561 if (type == "{")
11562 return pushContext(state, stream, "restricted_atBlock");
11563 if (type == "word" && state.stateArg == "@counter-style") {
11564 override = "variable";
11565 return "restricted_atBlock_before";
11566 }
11567 return pass(type, stream, state);
11568 };
11569
11570 states.restricted_atBlock = function(type, stream, state) {
11571 if (type == "}") {
11572 state.stateArg = null;
11573 return popContext(state);
11574 }
11575 if (type == "word") {
11576 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11577 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11578 override = "error";
11579 else
11580 override = "property";
11581 return "maybeprop";
11582 }
11583 return "restricted_atBlock";
11584 };
11585
11586 states.keyframes = function(type, stream, state) {
11587 if (type == "word") { override = "variable"; return "keyframes"; }
11588 if (type == "{") return pushContext(state, stream, "top");
11589 return pass(type, stream, state);
11590 };
11591
11592 states.at = function(type, stream, state) {
11593 if (type == ";") return popContext(state);
11594 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11595 if (type == "word") override = "tag";
11596 else if (type == "hash") override = "builtin";
11597 return "at";
11598 };
11599
11600 states.interpolation = function(type, stream, state) {
11601 if (type == "}") return popContext(state);
11602 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11603 if (type == "word") override = "variable";
11604 else if (type != "variable" && type != "(" && type != ")") override = "error";
11605 return "interpolation";
11606 };
11607
11608 return {
11609 startState: function(base) {
11610 return {tokenize: null,
11611 state: inline ? "block" : "top",
11612 stateArg: null,
11613 context: new Context(inline ? "block" : "top", base || 0, null)};
11614 },
11615
11616 token: function(stream, state) {
11617 if (!state.tokenize && stream.eatSpace()) return null;
11618 var style = (state.tokenize || tokenBase)(stream, state);
11619 if (style && typeof style == "object") {
11620 type = style[1];
11621 style = style[0];
11622 }
11623 override = style;
11624 if (type != "comment")
11625 state.state = states[state.state](type, stream, state);
11626 return override;
11627 },
11628
11629 indent: function(state, textAfter) {
11630 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11631 var indent = cx.indent;
11632 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11633 if (cx.prev) {
11634 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11635 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11636 // Resume indentation from parent context.
11637 cx = cx.prev;
11638 indent = cx.indent;
11639 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11640 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11641 // Dedent relative to current context.
11642 indent = Math.max(0, cx.indent - indentUnit);
11643 }
11644 }
11645 return indent;
11646 },
11647
11648 electricChars: "}",
11649 blockCommentStart: "/*",
11650 blockCommentEnd: "*/",
11651 blockCommentContinue: " * ",
11652 lineComment: lineComment,
11653 fold: "brace"
11654 };
11655 });
11656
11657 function keySet(array) {
11658 var keys = {};
11659 for (var i = 0; i < array.length; ++i) {
11660 keys[array[i].toLowerCase()] = true;
11661 }
11662 return keys;
11663 }
11664
11665 var documentTypes_ = [
11666 "domain", "regexp", "url", "url-prefix"
11667 ], documentTypes = keySet(documentTypes_);
11668
11669 var mediaTypes_ = [
11670 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11671 "tty", "tv", "embossed"
11672 ], mediaTypes = keySet(mediaTypes_);
11673
11674 var mediaFeatures_ = [
11675 "width", "min-width", "max-width", "height", "min-height", "max-height",
11676 "device-width", "min-device-width", "max-device-width", "device-height",
11677 "min-device-height", "max-device-height", "aspect-ratio",
11678 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11679 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11680 "max-color", "color-index", "min-color-index", "max-color-index",
11681 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11682 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11683 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11684 "pointer", "any-pointer", "hover", "any-hover"
11685 ], mediaFeatures = keySet(mediaFeatures_);
11686
11687 var mediaValueKeywords_ = [
11688 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11689 "interlace", "progressive"
11690 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11691
11692 var propertyKeywords_ = [
11693 "align-content", "align-items", "align-self", "alignment-adjust",
11694 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11695 "animation-direction", "animation-duration", "animation-fill-mode",
11696 "animation-iteration-count", "animation-name", "animation-play-state",
11697 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11698 "background", "background-attachment", "background-blend-mode", "background-clip",
11699 "background-color", "background-image", "background-origin", "background-position",
11700 "background-repeat", "background-size", "baseline-shift", "binding",
11701 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11702 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11703 "border-bottom-left-radius", "border-bottom-right-radius",
11704 "border-bottom-style", "border-bottom-width", "border-collapse",
11705 "border-color", "border-image", "border-image-outset",
11706 "border-image-repeat", "border-image-slice", "border-image-source",
11707 "border-image-width", "border-left", "border-left-color",
11708 "border-left-style", "border-left-width", "border-radius", "border-right",
11709 "border-right-color", "border-right-style", "border-right-width",
11710 "border-spacing", "border-style", "border-top", "border-top-color",
11711 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11712 "border-top-width", "border-width", "bottom", "box-decoration-break",
11713 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11714 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11715 "column-fill", "column-gap", "column-rule", "column-rule-color",
11716 "column-rule-style", "column-rule-width", "column-span", "column-width",
11717 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11718 "cue-after", "cue-before", "cursor", "direction", "display",
11719 "dominant-baseline", "drop-initial-after-adjust",
11720 "drop-initial-after-align", "drop-initial-before-adjust",
11721 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11722 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11723 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11724 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11725 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11726 "font-stretch", "font-style", "font-synthesis", "font-variant",
11727 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11728 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11729 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11730 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11731 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11732 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11733 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11734 "icon", "image-orientation", "image-rendering", "image-resolution",
11735 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11736 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11737 "line-stacking-shift", "line-stacking-strategy", "list-style",
11738 "list-style-image", "list-style-position", "list-style-type", "margin",
11739 "margin-bottom", "margin-left", "margin-right", "margin-top",
11740 "marks", "marquee-direction", "marquee-loop",
11741 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11742 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
11743 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11744 "opacity", "order", "orphans", "outline",
11745 "outline-color", "outline-offset", "outline-style", "outline-width",
11746 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11747 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11748 "page", "page-break-after", "page-break-before", "page-break-inside",
11749 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11750 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11751 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11752 "region-break-before", "region-break-inside", "region-fragment",
11753 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11754 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11755 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11756 "shape-outside", "size", "speak", "speak-as", "speak-header",
11757 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11758 "tab-size", "table-layout", "target", "target-name", "target-new",
11759 "target-position", "text-align", "text-align-last", "text-decoration",
11760 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11761 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11762 "text-emphasis-position", "text-emphasis-style", "text-height",
11763 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11764 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11765 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11766 "transition", "transition-delay", "transition-duration",
11767 "transition-property", "transition-timing-function", "unicode-bidi",
11768 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11769 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11770 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11771 "word-spacing", "word-wrap", "z-index",
11772 // SVG-specific
11773 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11774 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11775 "color-interpolation", "color-interpolation-filters",
11776 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11777 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11778 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11779 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11780 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11781 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11782 ], propertyKeywords = keySet(propertyKeywords_);
11783
11784 var nonStandardPropertyKeywords_ = [
11785 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11786 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11787 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11788 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11789 "searchfield-results-decoration", "zoom"
11790 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11791
11792 var fontProperties_ = [
11793 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11794 "font-stretch", "font-weight", "font-style"
11795 ], fontProperties = keySet(fontProperties_);
11796
11797 var counterDescriptors_ = [
11798 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11799 "speak-as", "suffix", "symbols", "system"
11800 ], counterDescriptors = keySet(counterDescriptors_);
11801
11802 var colorKeywords_ = [
11803 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11804 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11805 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11806 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11807 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11808 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11809 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11810 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11811 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11812 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11813 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11814 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11815 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11816 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11817 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11818 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11819 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11820 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11821 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11822 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11823 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11824 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11825 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11826 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11827 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11828 "whitesmoke", "yellow", "yellowgreen"
11829 ], colorKeywords = keySet(colorKeywords_);
11830
11831 var valueKeywords_ = [
11832 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11833 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11834 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11835 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11836 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11837 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11838 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11839 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11840 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11841 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11842 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11843 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11844 "compact", "condensed", "contain", "content", "contents",
11845 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11846 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11847 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11848 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11849 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11850 "dot-dash", "dot-dot-dash",
11851 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11852 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11853 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11854 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11855 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11856 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11857 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11858 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11859 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11860 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11861 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11862 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11863 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11864 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11865 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11866 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11867 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11868 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11869 "katakana", "katakana-iroha", "keep-all", "khmer",
11870 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11871 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11872 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11873 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11874 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11875 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11876 "media-controls-background", "media-current-time-display",
11877 "media-fullscreen-button", "media-mute-button", "media-play-button",
11878 "media-return-to-realtime-button", "media-rewind-button",
11879 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11880 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11881 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11882 "menu", "menulist", "menulist-button", "menulist-text",
11883 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11884 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11885 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11886 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11887 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11888 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11889 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11890 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11891 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11892 "progress", "push-button", "radial-gradient", "radio", "read-only",
11893 "read-write", "read-write-plaintext-only", "rectangle", "region",
11894 "relative", "repeat", "repeating-linear-gradient",
11895 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
11896 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
11897 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
11898 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
11899 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
11900 "searchfield-cancel-button", "searchfield-decoration",
11901 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
11902 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
11903 "simp-chinese-formal", "simp-chinese-informal", "single",
11904 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
11905 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
11906 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
11907 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
11908 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
11909 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
11910 "table-caption", "table-cell", "table-column", "table-column-group",
11911 "table-footer-group", "table-header-group", "table-row", "table-row-group",
11912 "tamil",
11913 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
11914 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
11915 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
11916 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
11917 "trad-chinese-formal", "trad-chinese-informal", "transform",
11918 "translate", "translate3d", "translateX", "translateY", "translateZ",
11919 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
11920 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
11921 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
11922 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
11923 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
11924 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
11925 "xx-large", "xx-small"
11926 ], valueKeywords = keySet(valueKeywords_);
11927
11928 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
11929 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
11930 .concat(valueKeywords_);
11931 CodeMirror.registerHelper("hintWords", "css", allWords);
11932
11933 function tokenCComment(stream, state) {
11934 var maybeEnd = false, ch;
11935 while ((ch = stream.next()) != null) {
11936 if (maybeEnd && ch == "/") {
11937 state.tokenize = null;
11938 break;
11939 }
11940 maybeEnd = (ch == "*");
11941 }
11942 return ["comment", "comment"];
11943 }
11944
11945 CodeMirror.defineMIME("text/css", {
11946 documentTypes: documentTypes,
11947 mediaTypes: mediaTypes,
11948 mediaFeatures: mediaFeatures,
11949 mediaValueKeywords: mediaValueKeywords,
11950 propertyKeywords: propertyKeywords,
11951 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11952 fontProperties: fontProperties,
11953 counterDescriptors: counterDescriptors,
11954 colorKeywords: colorKeywords,
11955 valueKeywords: valueKeywords,
11956 tokenHooks: {
11957 "/": function(stream, state) {
11958 if (!stream.eat("*")) return false;
11959 state.tokenize = tokenCComment;
11960 return tokenCComment(stream, state);
11961 }
11962 },
11963 name: "css"
11964 });
11965
11966 CodeMirror.defineMIME("text/x-scss", {
11967 mediaTypes: mediaTypes,
11968 mediaFeatures: mediaFeatures,
11969 mediaValueKeywords: mediaValueKeywords,
11970 propertyKeywords: propertyKeywords,
11971 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11972 colorKeywords: colorKeywords,
11973 valueKeywords: valueKeywords,
11974 fontProperties: fontProperties,
11975 allowNested: true,
11976 lineComment: "//",
11977 tokenHooks: {
11978 "/": function(stream, state) {
11979 if (stream.eat("/")) {
11980 stream.skipToEnd();
11981 return ["comment", "comment"];
11982 } else if (stream.eat("*")) {
11983 state.tokenize = tokenCComment;
11984 return tokenCComment(stream, state);
11985 } else {
11986 return ["operator", "operator"];
11987 }
11988 },
11989 ":": function(stream) {
11990 if (stream.match(/\s*\{/, false))
11991 return [null, null]
11992 return false;
11993 },
11994 "$": function(stream) {
11995 stream.match(/^[\w-]+/);
11996 if (stream.match(/^\s*:/, false))
11997 return ["variable-2", "variable-definition"];
11998 return ["variable-2", "variable"];
11999 },
12000 "#": function(stream) {
12001 if (!stream.eat("{")) return false;
12002 return [null, "interpolation"];
12003 }
12004 },
12005 name: "css",
12006 helperType: "scss"
12007 });
12008
12009 CodeMirror.defineMIME("text/x-less", {
12010 mediaTypes: mediaTypes,
12011 mediaFeatures: mediaFeatures,
12012 mediaValueKeywords: mediaValueKeywords,
12013 propertyKeywords: propertyKeywords,
12014 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12015 colorKeywords: colorKeywords,
12016 valueKeywords: valueKeywords,
12017 fontProperties: fontProperties,
12018 allowNested: true,
12019 lineComment: "//",
12020 tokenHooks: {
12021 "/": function(stream, state) {
12022 if (stream.eat("/")) {
12023 stream.skipToEnd();
12024 return ["comment", "comment"];
12025 } else if (stream.eat("*")) {
12026 state.tokenize = tokenCComment;
12027 return tokenCComment(stream, state);
12028 } else {
12029 return ["operator", "operator"];
12030 }
12031 },
12032 "@": function(stream) {
12033 if (stream.eat("{")) return [null, "interpolation"];
12034 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12035 stream.eatWhile(/[\w\\\-]/);
12036 if (stream.match(/^\s*:/, false))
12037 return ["variable-2", "variable-definition"];
12038 return ["variable-2", "variable"];
12039 },
12040 "&": function() {
12041 return ["atom", "atom"];
12042 }
12043 },
12044 name: "css",
12045 helperType: "less"
12046 });
12047
12048 CodeMirror.defineMIME("text/x-gss", {
12049 documentTypes: documentTypes,
12050 mediaTypes: mediaTypes,
12051 mediaFeatures: mediaFeatures,
12052 propertyKeywords: propertyKeywords,
12053 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12054 fontProperties: fontProperties,
12055 counterDescriptors: counterDescriptors,
12056 colorKeywords: colorKeywords,
12057 valueKeywords: valueKeywords,
12058 supportsAtComponent: true,
12059 tokenHooks: {
12060 "/": function(stream, state) {
12061 if (!stream.eat("*")) return false;
12062 state.tokenize = tokenCComment;
12063 return tokenCComment(stream, state);
12064 }
12065 },
12066 name: "css",
12067 helperType: "gss"
12068 });
12069
12070 });
12071
12072 },{"../../lib/codemirror":14}],16:[function(require,module,exports){
12073 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12074 // Distributed under an MIT license: http://codemirror.net/LICENSE
12075
12076 (function(mod) {
12077 if (typeof exports == "object" && typeof module == "object") // CommonJS
12078 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
12079 else if (typeof define == "function" && define.amd) // AMD
12080 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
12081 else // Plain browser env
12082 mod(CodeMirror);
12083 })(function(CodeMirror) {
12084 "use strict";
12085
12086 var defaultTags = {
12087 script: [
12088 ["lang", /(javascript|babel)/i, "javascript"],
12089 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
12090 ["type", /./, "text/plain"],
12091 [null, null, "javascript"]
12092 ],
12093 style: [
12094 ["lang", /^css$/i, "css"],
12095 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
12096 ["type", /./, "text/plain"],
12097 [null, null, "css"]
12098 ]
12099 };
12100
12101 function maybeBackup(stream, pat, style) {
12102 var cur = stream.current(), close = cur.search(pat);
12103 if (close > -1) {
12104 stream.backUp(cur.length - close);
12105 } else if (cur.match(/<\/?$/)) {
12106 stream.backUp(cur.length);
12107 if (!stream.match(pat, false)) stream.match(cur);
12108 }
12109 return style;
12110 }
12111
12112 var attrRegexpCache = {};
12113 function getAttrRegexp(attr) {
12114 var regexp = attrRegexpCache[attr];
12115 if (regexp) return regexp;
12116 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12117 }
12118
12119 function getAttrValue(text, attr) {
12120 var match = text.match(getAttrRegexp(attr))
12121 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12122 }
12123
12124 function getTagRegexp(tagName, anchored) {
12125 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12126 }
12127
12128 function addTags(from, to) {
12129 for (var tag in from) {
12130 var dest = to[tag] || (to[tag] = []);
12131 var source = from[tag];
12132 for (var i = source.length - 1; i >= 0; i--)
12133 dest.unshift(source[i])
12134 }
12135 }
12136
12137 function findMatchingMode(tagInfo, tagText) {
12138 for (var i = 0; i < tagInfo.length; i++) {
12139 var spec = tagInfo[i];
12140 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12141 }
12142 }
12143
12144 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12145 var htmlMode = CodeMirror.getMode(config, {
12146 name: "xml",
12147 htmlMode: true,
12148 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12149 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12150 });
12151
12152 var tags = {};
12153 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12154 addTags(defaultTags, tags);
12155 if (configTags) addTags(configTags, tags);
12156 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12157 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12158
12159 function html(stream, state) {
12160 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12161 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12162 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12163 tags.hasOwnProperty(tagName)) {
12164 state.inTag = tagName + " "
12165 } else if (state.inTag && tag && />$/.test(stream.current())) {
12166 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12167 state.inTag = null
12168 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12169 var mode = CodeMirror.getMode(config, modeSpec)
12170 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12171 state.token = function (stream, state) {
12172 if (stream.match(endTagA, false)) {
12173 state.token = html;
12174 state.localState = state.localMode = null;
12175 return null;
12176 }
12177 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12178 };
12179 state.localMode = mode;
12180 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
12181 } else if (state.inTag) {
12182 state.inTag += stream.current()
12183 if (stream.eol()) state.inTag += " "
12184 }
12185 return style;
12186 };
12187
12188 return {
12189 startState: function () {
12190 var state = CodeMirror.startState(htmlMode);
12191 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12192 },
12193
12194 copyState: function (state) {
12195 var local;
12196 if (state.localState) {
12197 local = CodeMirror.copyState(state.localMode, state.localState);
12198 }
12199 return {token: state.token, inTag: state.inTag,
12200 localMode: state.localMode, localState: local,
12201 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12202 },
12203
12204 token: function (stream, state) {
12205 return state.token(stream, state);
12206 },
12207
12208 indent: function (state, textAfter, line) {
12209 if (!state.localMode || /^\s*<\//.test(textAfter))
12210 return htmlMode.indent(state.htmlState, textAfter);
12211 else if (state.localMode.indent)
12212 return state.localMode.indent(state.localState, textAfter, line);
12213 else
12214 return CodeMirror.Pass;
12215 },
12216
12217 innerMode: function (state) {
12218 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12219 }
12220 };
12221 }, "xml", "javascript", "css");
12222
12223 CodeMirror.defineMIME("text/html", "htmlmixed");
12224 });
12225
12226 },{"../../lib/codemirror":14,"../css/css":15,"../javascript/javascript":17,"../xml/xml":18}],17:[function(require,module,exports){
12227 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12228 // Distributed under an MIT license: http://codemirror.net/LICENSE
12229
12230 (function(mod) {
12231 if (typeof exports == "object" && typeof module == "object") // CommonJS
12232 mod(require("../../lib/codemirror"));
12233 else if (typeof define == "function" && define.amd) // AMD
12234 define(["../../lib/codemirror"], mod);
12235 else // Plain browser env
12236 mod(CodeMirror);
12237 })(function(CodeMirror) {
12238 "use strict";
12239
12240 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12241 var indentUnit = config.indentUnit;
12242 var statementIndent = parserConfig.statementIndent;
12243 var jsonldMode = parserConfig.jsonld;
12244 var jsonMode = parserConfig.json || jsonldMode;
12245 var isTS = parserConfig.typescript;
12246 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12247
12248 // Tokenizer
12249
12250 var keywords = function(){
12251 function kw(type) {return {type: type, style: "keyword"};}
12252 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12253 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12254
12255 return {
12256 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12257 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12258 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12259 "function": kw("function"), "catch": kw("catch"),
12260 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12261 "in": operator, "typeof": operator, "instanceof": operator,
12262 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12263 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12264 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12265 "await": C
12266 };
12267 }();
12268
12269 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12270 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12271
12272 function readRegexp(stream) {
12273 var escaped = false, next, inSet = false;
12274 while ((next = stream.next()) != null) {
12275 if (!escaped) {
12276 if (next == "/" && !inSet) return;
12277 if (next == "[") inSet = true;
12278 else if (inSet && next == "]") inSet = false;
12279 }
12280 escaped = !escaped && next == "\\";
12281 }
12282 }
12283
12284 // Used as scratch variables to communicate multiple values without
12285 // consing up tons of objects.
12286 var type, content;
12287 function ret(tp, style, cont) {
12288 type = tp; content = cont;
12289 return style;
12290 }
12291 function tokenBase(stream, state) {
12292 var ch = stream.next();
12293 if (ch == '"' || ch == "'") {
12294 state.tokenize = tokenString(ch);
12295 return state.tokenize(stream, state);
12296 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
12297 return ret("number", "number");
12298 } else if (ch == "." && stream.match("..")) {
12299 return ret("spread", "meta");
12300 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12301 return ret(ch);
12302 } else if (ch == "=" && stream.eat(">")) {
12303 return ret("=>", "operator");
12304 } else if (ch == "0" && stream.eat(/x/i)) {
12305 stream.eatWhile(/[\da-f]/i);
12306 return ret("number", "number");
12307 } else if (ch == "0" && stream.eat(/o/i)) {
12308 stream.eatWhile(/[0-7]/i);
12309 return ret("number", "number");
12310 } else if (ch == "0" && stream.eat(/b/i)) {
12311 stream.eatWhile(/[01]/i);
12312 return ret("number", "number");
12313 } else if (/\d/.test(ch)) {
12314 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
12315 return ret("number", "number");
12316 } else if (ch == "/") {
12317 if (stream.eat("*")) {
12318 state.tokenize = tokenComment;
12319 return tokenComment(stream, state);
12320 } else if (stream.eat("/")) {
12321 stream.skipToEnd();
12322 return ret("comment", "comment");
12323 } else if (expressionAllowed(stream, state, 1)) {
12324 readRegexp(stream);
12325 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
12326 return ret("regexp", "string-2");
12327 } else {
12328 stream.eat("=");
12329 return ret("operator", "operator", stream.current());
12330 }
12331 } else if (ch == "`") {
12332 state.tokenize = tokenQuasi;
12333 return tokenQuasi(stream, state);
12334 } else if (ch == "#") {
12335 stream.skipToEnd();
12336 return ret("error", "error");
12337 } else if (isOperatorChar.test(ch)) {
12338 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12339 if (stream.eat("=")) {
12340 if (ch == "!" || ch == "=") stream.eat("=")
12341 } else if (/[<>*+\-]/.test(ch)) {
12342 stream.eat(ch)
12343 if (ch == ">") stream.eat(ch)
12344 }
12345 }
12346 return ret("operator", "operator", stream.current());
12347 } else if (wordRE.test(ch)) {
12348 stream.eatWhile(wordRE);
12349 var word = stream.current()
12350 if (state.lastType != ".") {
12351 if (keywords.propertyIsEnumerable(word)) {
12352 var kw = keywords[word]
12353 return ret(kw.type, kw.style, word)
12354 }
12355 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false))
12356 return ret("async", "keyword", word)
12357 }
12358 return ret("variable", "variable", word)
12359 }
12360 }
12361
12362 function tokenString(quote) {
12363 return function(stream, state) {
12364 var escaped = false, next;
12365 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12366 state.tokenize = tokenBase;
12367 return ret("jsonld-keyword", "meta");
12368 }
12369 while ((next = stream.next()) != null) {
12370 if (next == quote && !escaped) break;
12371 escaped = !escaped && next == "\\";
12372 }
12373 if (!escaped) state.tokenize = tokenBase;
12374 return ret("string", "string");
12375 };
12376 }
12377
12378 function tokenComment(stream, state) {
12379 var maybeEnd = false, ch;
12380 while (ch = stream.next()) {
12381 if (ch == "/" && maybeEnd) {
12382 state.tokenize = tokenBase;
12383 break;
12384 }
12385 maybeEnd = (ch == "*");
12386 }
12387 return ret("comment", "comment");
12388 }
12389
12390 function tokenQuasi(stream, state) {
12391 var escaped = false, next;
12392 while ((next = stream.next()) != null) {
12393 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12394 state.tokenize = tokenBase;
12395 break;
12396 }
12397 escaped = !escaped && next == "\\";
12398 }
12399 return ret("quasi", "string-2", stream.current());
12400 }
12401
12402 var brackets = "([{}])";
12403 // This is a crude lookahead trick to try and notice that we're
12404 // parsing the argument patterns for a fat-arrow function before we
12405 // actually hit the arrow token. It only works if the arrow is on
12406 // the same line as the arguments and there's no strange noise
12407 // (comments) in between. Fallback is to only notice when we hit the
12408 // arrow, and not declare the arguments as locals for the arrow
12409 // body.
12410 function findFatArrow(stream, state) {
12411 if (state.fatArrowAt) state.fatArrowAt = null;
12412 var arrow = stream.string.indexOf("=>", stream.start);
12413 if (arrow < 0) return;
12414
12415 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12416 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12417 if (m) arrow = m.index
12418 }
12419
12420 var depth = 0, sawSomething = false;
12421 for (var pos = arrow - 1; pos >= 0; --pos) {
12422 var ch = stream.string.charAt(pos);
12423 var bracket = brackets.indexOf(ch);
12424 if (bracket >= 0 && bracket < 3) {
12425 if (!depth) { ++pos; break; }
12426 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12427 } else if (bracket >= 3 && bracket < 6) {
12428 ++depth;
12429 } else if (wordRE.test(ch)) {
12430 sawSomething = true;
12431 } else if (/["'\/]/.test(ch)) {
12432 return;
12433 } else if (sawSomething && !depth) {
12434 ++pos;
12435 break;
12436 }
12437 }
12438 if (sawSomething && !depth) state.fatArrowAt = pos;
12439 }
12440
12441 // Parser
12442
12443 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12444
12445 function JSLexical(indented, column, type, align, prev, info) {
12446 this.indented = indented;
12447 this.column = column;
12448 this.type = type;
12449 this.prev = prev;
12450 this.info = info;
12451 if (align != null) this.align = align;
12452 }
12453
12454 function inScope(state, varname) {
12455 for (var v = state.localVars; v; v = v.next)
12456 if (v.name == varname) return true;
12457 for (var cx = state.context; cx; cx = cx.prev) {
12458 for (var v = cx.vars; v; v = v.next)
12459 if (v.name == varname) return true;
12460 }
12461 }
12462
12463 function parseJS(state, style, type, content, stream) {
12464 var cc = state.cc;
12465 // Communicate our context to the combinators.
12466 // (Less wasteful than consing up a hundred closures on every call.)
12467 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12468
12469 if (!state.lexical.hasOwnProperty("align"))
12470 state.lexical.align = true;
12471
12472 while(true) {
12473 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12474 if (combinator(type, content)) {
12475 while(cc.length && cc[cc.length - 1].lex)
12476 cc.pop()();
12477 if (cx.marked) return cx.marked;
12478 if (type == "variable" && inScope(state, content)) return "variable-2";
12479 return style;
12480 }
12481 }
12482 }
12483
12484 // Combinator utils
12485
12486 var cx = {state: null, column: null, marked: null, cc: null};
12487 function pass() {
12488 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12489 }
12490 function cont() {
12491 pass.apply(null, arguments);
12492 return true;
12493 }
12494 function register(varname) {
12495 function inList(list) {
12496 for (var v = list; v; v = v.next)
12497 if (v.name == varname) return true;
12498 return false;
12499 }
12500 var state = cx.state;
12501 cx.marked = "def";
12502 if (state.context) {
12503 if (inList(state.localVars)) return;
12504 state.localVars = {name: varname, next: state.localVars};
12505 } else {
12506 if (inList(state.globalVars)) return;
12507 if (parserConfig.globalVars)
12508 state.globalVars = {name: varname, next: state.globalVars};
12509 }
12510 }
12511
12512 function isModifier(name) {
12513 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12514 }
12515
12516 // Combinators
12517
12518 var defaultVars = {name: "this", next: {name: "arguments"}};
12519 function pushcontext() {
12520 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
12521 cx.state.localVars = defaultVars;
12522 }
12523 function popcontext() {
12524 cx.state.localVars = cx.state.context.vars;
12525 cx.state.context = cx.state.context.prev;
12526 }
12527 function pushlex(type, info) {
12528 var result = function() {
12529 var state = cx.state, indent = state.indented;
12530 if (state.lexical.type == "stat") indent = state.lexical.indented;
12531 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12532 indent = outer.indented;
12533 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12534 };
12535 result.lex = true;
12536 return result;
12537 }
12538 function poplex() {
12539 var state = cx.state;
12540 if (state.lexical.prev) {
12541 if (state.lexical.type == ")")
12542 state.indented = state.lexical.indented;
12543 state.lexical = state.lexical.prev;
12544 }
12545 }
12546 poplex.lex = true;
12547
12548 function expect(wanted) {
12549 function exp(type) {
12550 if (type == wanted) return cont();
12551 else if (wanted == ";") return pass();
12552 else return cont(exp);
12553 };
12554 return exp;
12555 }
12556
12557 function statement(type, value) {
12558 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
12559 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12560 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12561 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12562 if (type == "debugger") return cont(expect(";"));
12563 if (type == "{") return cont(pushlex("}"), block, poplex);
12564 if (type == ";") return cont();
12565 if (type == "if") {
12566 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12567 cx.state.cc.pop()();
12568 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12569 }
12570 if (type == "function") return cont(functiondef);
12571 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12572 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
12573 if (type == "variable") {
12574 if (isTS && value == "declare") {
12575 cx.marked = "keyword"
12576 return cont(statement)
12577 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12578 cx.marked = "keyword"
12579 if (value == "enum") return cont(enumdef);
12580 else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
12581 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12582 } else if (isTS && value == "namespace") {
12583 cx.marked = "keyword"
12584 return cont(pushlex("form"), expression, block, poplex)
12585 } else {
12586 return cont(pushlex("stat"), maybelabel);
12587 }
12588 }
12589 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
12590 block, poplex, poplex);
12591 if (type == "case") return cont(expression, expect(":"));
12592 if (type == "default") return cont(expect(":"));
12593 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
12594 statement, poplex, popcontext);
12595 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12596 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12597 if (type == "async") return cont(statement)
12598 if (value == "@") return cont(expression, statement)
12599 return pass(pushlex("stat"), expression, expect(";"), poplex);
12600 }
12601 function expression(type, value) {
12602 return expressionInner(type, value, false);
12603 }
12604 function expressionNoComma(type, value) {
12605 return expressionInner(type, value, true);
12606 }
12607 function parenExpr(type) {
12608 if (type != "(") return pass()
12609 return cont(pushlex(")"), expression, expect(")"), poplex)
12610 }
12611 function expressionInner(type, value, noComma) {
12612 if (cx.state.fatArrowAt == cx.stream.start) {
12613 var body = noComma ? arrowBodyNoComma : arrowBody;
12614 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12615 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12616 }
12617
12618 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12619 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12620 if (type == "function") return cont(functiondef, maybeop);
12621 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12622 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12623 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12624 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12625 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12626 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12627 if (type == "quasi") return pass(quasi, maybeop);
12628 if (type == "new") return cont(maybeTarget(noComma));
12629 return cont();
12630 }
12631 function maybeexpression(type) {
12632 if (type.match(/[;\}\)\],]/)) return pass();
12633 return pass(expression);
12634 }
12635
12636 function maybeoperatorComma(type, value) {
12637 if (type == ",") return cont(expression);
12638 return maybeoperatorNoComma(type, value, false);
12639 }
12640 function maybeoperatorNoComma(type, value, noComma) {
12641 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12642 var expr = noComma == false ? expression : expressionNoComma;
12643 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12644 if (type == "operator") {
12645 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12646 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12647 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12648 if (value == "?") return cont(expression, expect(":"), expr);
12649 return cont(expr);
12650 }
12651 if (type == "quasi") { return pass(quasi, me); }
12652 if (type == ";") return;
12653 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12654 if (type == ".") return cont(property, me);
12655 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12656 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12657 if (type == "regexp") {
12658 cx.state.lastType = cx.marked = "operator"
12659 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12660 return cont(expr)
12661 }
12662 }
12663 function quasi(type, value) {
12664 if (type != "quasi") return pass();
12665 if (value.slice(value.length - 2) != "${") return cont(quasi);
12666 return cont(expression, continueQuasi);
12667 }
12668 function continueQuasi(type) {
12669 if (type == "}") {
12670 cx.marked = "string-2";
12671 cx.state.tokenize = tokenQuasi;
12672 return cont(quasi);
12673 }
12674 }
12675 function arrowBody(type) {
12676 findFatArrow(cx.stream, cx.state);
12677 return pass(type == "{" ? statement : expression);
12678 }
12679 function arrowBodyNoComma(type) {
12680 findFatArrow(cx.stream, cx.state);
12681 return pass(type == "{" ? statement : expressionNoComma);
12682 }
12683 function maybeTarget(noComma) {
12684 return function(type) {
12685 if (type == ".") return cont(noComma ? targetNoComma : target);
12686 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12687 else return pass(noComma ? expressionNoComma : expression);
12688 };
12689 }
12690 function target(_, value) {
12691 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12692 }
12693 function targetNoComma(_, value) {
12694 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12695 }
12696 function maybelabel(type) {
12697 if (type == ":") return cont(poplex, statement);
12698 return pass(maybeoperatorComma, expect(";"), poplex);
12699 }
12700 function property(type) {
12701 if (type == "variable") {cx.marked = "property"; return cont();}
12702 }
12703 function objprop(type, value) {
12704 if (type == "async") {
12705 cx.marked = "property";
12706 return cont(objprop);
12707 } else if (type == "variable" || cx.style == "keyword") {
12708 cx.marked = "property";
12709 if (value == "get" || value == "set") return cont(getterSetter);
12710 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12711 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12712 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12713 return cont(afterprop);
12714 } else if (type == "number" || type == "string") {
12715 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12716 return cont(afterprop);
12717 } else if (type == "jsonld-keyword") {
12718 return cont(afterprop);
12719 } else if (isTS && isModifier(value)) {
12720 cx.marked = "keyword"
12721 return cont(objprop)
12722 } else if (type == "[") {
12723 return cont(expression, maybetype, expect("]"), afterprop);
12724 } else if (type == "spread") {
12725 return cont(expressionNoComma, afterprop);
12726 } else if (value == "*") {
12727 cx.marked = "keyword";
12728 return cont(objprop);
12729 } else if (type == ":") {
12730 return pass(afterprop)
12731 }
12732 }
12733 function getterSetter(type) {
12734 if (type != "variable") return pass(afterprop);
12735 cx.marked = "property";
12736 return cont(functiondef);
12737 }
12738 function afterprop(type) {
12739 if (type == ":") return cont(expressionNoComma);
12740 if (type == "(") return pass(functiondef);
12741 }
12742 function commasep(what, end, sep) {
12743 function proceed(type, value) {
12744 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12745 var lex = cx.state.lexical;
12746 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12747 return cont(function(type, value) {
12748 if (type == end || value == end) return pass()
12749 return pass(what)
12750 }, proceed);
12751 }
12752 if (type == end || value == end) return cont();
12753 return cont(expect(end));
12754 }
12755 return function(type, value) {
12756 if (type == end || value == end) return cont();
12757 return pass(what, proceed);
12758 };
12759 }
12760 function contCommasep(what, end, info) {
12761 for (var i = 3; i < arguments.length; i++)
12762 cx.cc.push(arguments[i]);
12763 return cont(pushlex(end, info), commasep(what, end), poplex);
12764 }
12765 function block(type) {
12766 if (type == "}") return cont();
12767 return pass(statement, block);
12768 }
12769 function maybetype(type, value) {
12770 if (isTS) {
12771 if (type == ":") return cont(typeexpr);
12772 if (value == "?") return cont(maybetype);
12773 }
12774 }
12775 function mayberettype(type) {
12776 if (isTS && type == ":") {
12777 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12778 else return cont(typeexpr)
12779 }
12780 }
12781 function isKW(_, value) {
12782 if (value == "is") {
12783 cx.marked = "keyword"
12784 return cont()
12785 }
12786 }
12787 function typeexpr(type, value) {
12788 if (type == "variable" || value == "void") {
12789 if (value == "keyof") {
12790 cx.marked = "keyword"
12791 return cont(typeexpr)
12792 } else {
12793 cx.marked = "type"
12794 return cont(afterType)
12795 }
12796 }
12797 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12798 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12799 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12800 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
12801 }
12802 function maybeReturnType(type) {
12803 if (type == "=>") return cont(typeexpr)
12804 }
12805 function typeprop(type, value) {
12806 if (type == "variable" || cx.style == "keyword") {
12807 cx.marked = "property"
12808 return cont(typeprop)
12809 } else if (value == "?") {
12810 return cont(typeprop)
12811 } else if (type == ":") {
12812 return cont(typeexpr)
12813 } else if (type == "[") {
12814 return cont(expression, maybetype, expect("]"), typeprop)
12815 }
12816 }
12817 function typearg(type) {
12818 if (type == "variable") return cont(typearg)
12819 else if (type == ":") return cont(typeexpr)
12820 }
12821 function afterType(type, value) {
12822 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12823 if (value == "|" || type == ".") return cont(typeexpr)
12824 if (type == "[") return cont(expect("]"), afterType)
12825 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
12826 }
12827 function maybeTypeArgs(_, value) {
12828 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12829 }
12830 function typeparam() {
12831 return pass(typeexpr, maybeTypeDefault)
12832 }
12833 function maybeTypeDefault(_, value) {
12834 if (value == "=") return cont(typeexpr)
12835 }
12836 function vardef(_, value) {
12837 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
12838 return pass(pattern, maybetype, maybeAssign, vardefCont);
12839 }
12840 function pattern(type, value) {
12841 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
12842 if (type == "variable") { register(value); return cont(); }
12843 if (type == "spread") return cont(pattern);
12844 if (type == "[") return contCommasep(pattern, "]");
12845 if (type == "{") return contCommasep(proppattern, "}");
12846 }
12847 function proppattern(type, value) {
12848 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12849 register(value);
12850 return cont(maybeAssign);
12851 }
12852 if (type == "variable") cx.marked = "property";
12853 if (type == "spread") return cont(pattern);
12854 if (type == "}") return pass();
12855 return cont(expect(":"), pattern, maybeAssign);
12856 }
12857 function maybeAssign(_type, value) {
12858 if (value == "=") return cont(expressionNoComma);
12859 }
12860 function vardefCont(type) {
12861 if (type == ",") return cont(vardef);
12862 }
12863 function maybeelse(type, value) {
12864 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
12865 }
12866 function forspec(type) {
12867 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
12868 }
12869 function forspec1(type) {
12870 if (type == "var") return cont(vardef, expect(";"), forspec2);
12871 if (type == ";") return cont(forspec2);
12872 if (type == "variable") return cont(formaybeinof);
12873 return pass(expression, expect(";"), forspec2);
12874 }
12875 function formaybeinof(_type, value) {
12876 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12877 return cont(maybeoperatorComma, forspec2);
12878 }
12879 function forspec2(type, value) {
12880 if (type == ";") return cont(forspec3);
12881 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12882 return pass(expression, expect(";"), forspec3);
12883 }
12884 function forspec3(type) {
12885 if (type != ")") cont(expression);
12886 }
12887 function functiondef(type, value) {
12888 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
12889 if (type == "variable") {register(value); return cont(functiondef);}
12890 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
12891 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
12892 }
12893 function funarg(type, value) {
12894 if (value == "@") cont(expression, funarg)
12895 if (type == "spread") return cont(funarg);
12896 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
12897 return pass(pattern, maybetype, maybeAssign);
12898 }
12899 function classExpression(type, value) {
12900 // Class expressions may have an optional name.
12901 if (type == "variable") return className(type, value);
12902 return classNameAfter(type, value);
12903 }
12904 function className(type, value) {
12905 if (type == "variable") {register(value); return cont(classNameAfter);}
12906 }
12907 function classNameAfter(type, value) {
12908 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
12909 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
12910 if (value == "implements") cx.marked = "keyword";
12911 return cont(isTS ? typeexpr : expression, classNameAfter);
12912 }
12913 if (type == "{") return cont(pushlex("}"), classBody, poplex);
12914 }
12915 function classBody(type, value) {
12916 if (type == "async" ||
12917 (type == "variable" &&
12918 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
12919 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
12920 cx.marked = "keyword";
12921 return cont(classBody);
12922 }
12923 if (type == "variable" || cx.style == "keyword") {
12924 cx.marked = "property";
12925 return cont(isTS ? classfield : functiondef, classBody);
12926 }
12927 if (type == "[")
12928 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
12929 if (value == "*") {
12930 cx.marked = "keyword";
12931 return cont(classBody);
12932 }
12933 if (type == ";") return cont(classBody);
12934 if (type == "}") return cont();
12935 if (value == "@") return cont(expression, classBody)
12936 }
12937 function classfield(type, value) {
12938 if (value == "?") return cont(classfield)
12939 if (type == ":") return cont(typeexpr, maybeAssign)
12940 if (value == "=") return cont(expressionNoComma)
12941 return pass(functiondef)
12942 }
12943 function afterExport(type, value) {
12944 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
12945 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
12946 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
12947 return pass(statement);
12948 }
12949 function exportField(type, value) {
12950 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
12951 if (type == "variable") return pass(expressionNoComma, exportField);
12952 }
12953 function afterImport(type) {
12954 if (type == "string") return cont();
12955 return pass(importSpec, maybeMoreImports, maybeFrom);
12956 }
12957 function importSpec(type, value) {
12958 if (type == "{") return contCommasep(importSpec, "}");
12959 if (type == "variable") register(value);
12960 if (value == "*") cx.marked = "keyword";
12961 return cont(maybeAs);
12962 }
12963 function maybeMoreImports(type) {
12964 if (type == ",") return cont(importSpec, maybeMoreImports)
12965 }
12966 function maybeAs(_type, value) {
12967 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
12968 }
12969 function maybeFrom(_type, value) {
12970 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
12971 }
12972 function arrayLiteral(type) {
12973 if (type == "]") return cont();
12974 return pass(commasep(expressionNoComma, "]"));
12975 }
12976 function enumdef() {
12977 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
12978 }
12979 function enummember() {
12980 return pass(pattern, maybeAssign);
12981 }
12982
12983 function isContinuedStatement(state, textAfter) {
12984 return state.lastType == "operator" || state.lastType == "," ||
12985 isOperatorChar.test(textAfter.charAt(0)) ||
12986 /[,.]/.test(textAfter.charAt(0));
12987 }
12988
12989 function expressionAllowed(stream, state, backUp) {
12990 return state.tokenize == tokenBase &&
12991 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
12992 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
12993 }
12994
12995 // Interface
12996
12997 return {
12998 startState: function(basecolumn) {
12999 var state = {
13000 tokenize: tokenBase,
13001 lastType: "sof",
13002 cc: [],
13003 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
13004 localVars: parserConfig.localVars,
13005 context: parserConfig.localVars && {vars: parserConfig.localVars},
13006 indented: basecolumn || 0
13007 };
13008 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
13009 state.globalVars = parserConfig.globalVars;
13010 return state;
13011 },
13012
13013 token: function(stream, state) {
13014 if (stream.sol()) {
13015 if (!state.lexical.hasOwnProperty("align"))
13016 state.lexical.align = false;
13017 state.indented = stream.indentation();
13018 findFatArrow(stream, state);
13019 }
13020 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
13021 var style = state.tokenize(stream, state);
13022 if (type == "comment") return style;
13023 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
13024 return parseJS(state, style, type, content, stream);
13025 },
13026
13027 indent: function(state, textAfter) {
13028 if (state.tokenize == tokenComment) return CodeMirror.Pass;
13029 if (state.tokenize != tokenBase) return 0;
13030 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
13031 // Kludge to prevent 'maybelse' from blocking lexical scope pops
13032 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
13033 var c = state.cc[i];
13034 if (c == poplex) lexical = lexical.prev;
13035 else if (c != maybeelse) break;
13036 }
13037 while ((lexical.type == "stat" || lexical.type == "form") &&
13038 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
13039 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
13040 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
13041 lexical = lexical.prev;
13042 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
13043 lexical = lexical.prev;
13044 var type = lexical.type, closing = firstChar == type;
13045
13046 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
13047 else if (type == "form" && firstChar == "{") return lexical.indented;
13048 else if (type == "form") return lexical.indented + indentUnit;
13049 else if (type == "stat")
13050 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
13051 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
13052 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
13053 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
13054 else return lexical.indented + (closing ? 0 : indentUnit);
13055 },
13056
13057 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
13058 blockCommentStart: jsonMode ? null : "/*",
13059 blockCommentEnd: jsonMode ? null : "*/",
13060 blockCommentContinue: jsonMode ? null : " * ",
13061 lineComment: jsonMode ? null : "//",
13062 fold: "brace",
13063 closeBrackets: "()[]{}''\"\"``",
13064
13065 helperType: jsonMode ? "json" : "javascript",
13066 jsonldMode: jsonldMode,
13067 jsonMode: jsonMode,
13068
13069 expressionAllowed: expressionAllowed,
13070
13071 skipExpression: function(state) {
13072 var top = state.cc[state.cc.length - 1]
13073 if (top == expression || top == expressionNoComma) state.cc.pop()
13074 }
13075 };
13076 });
13077
13078 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
13079
13080 CodeMirror.defineMIME("text/javascript", "javascript");
13081 CodeMirror.defineMIME("text/ecmascript", "javascript");
13082 CodeMirror.defineMIME("application/javascript", "javascript");
13083 CodeMirror.defineMIME("application/x-javascript", "javascript");
13084 CodeMirror.defineMIME("application/ecmascript", "javascript");
13085 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
13086 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
13087 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
13088 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
13089 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
13090
13091 });
13092
13093 },{"../../lib/codemirror":14}],18:[function(require,module,exports){
13094 // CodeMirror, copyright (c) by Marijn Haverbeke and others
13095 // Distributed under an MIT license: http://codemirror.net/LICENSE
13096
13097 (function(mod) {
13098 if (typeof exports == "object" && typeof module == "object") // CommonJS
13099 mod(require("../../lib/codemirror"));
13100 else if (typeof define == "function" && define.amd) // AMD
13101 define(["../../lib/codemirror"], mod);
13102 else // Plain browser env
13103 mod(CodeMirror);
13104 })(function(CodeMirror) {
13105 "use strict";
13106
13107 var htmlConfig = {
13108 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13109 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13110 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13111 'track': true, 'wbr': true, 'menuitem': true},
13112 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13113 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13114 'th': true, 'tr': true},
13115 contextGrabbers: {
13116 'dd': {'dd': true, 'dt': true},
13117 'dt': {'dd': true, 'dt': true},
13118 'li': {'li': true},
13119 'option': {'option': true, 'optgroup': true},
13120 'optgroup': {'optgroup': true},
13121 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13122 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13123 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13124 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13125 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13126 'rp': {'rp': true, 'rt': true},
13127 'rt': {'rp': true, 'rt': true},
13128 'tbody': {'tbody': true, 'tfoot': true},
13129 'td': {'td': true, 'th': true},
13130 'tfoot': {'tbody': true},
13131 'th': {'td': true, 'th': true},
13132 'thead': {'tbody': true, 'tfoot': true},
13133 'tr': {'tr': true}
13134 },
13135 doNotIndent: {"pre": true},
13136 allowUnquoted: true,
13137 allowMissing: true,
13138 caseFold: true
13139 }
13140
13141 var xmlConfig = {
13142 autoSelfClosers: {},
13143 implicitlyClosed: {},
13144 contextGrabbers: {},
13145 doNotIndent: {},
13146 allowUnquoted: false,
13147 allowMissing: false,
13148 allowMissingTagName: false,
13149 caseFold: false
13150 }
13151
13152 CodeMirror.defineMode("xml", function(editorConf, config_) {
13153 var indentUnit = editorConf.indentUnit
13154 var config = {}
13155 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13156 for (var prop in defaults) config[prop] = defaults[prop]
13157 for (var prop in config_) config[prop] = config_[prop]
13158
13159 // Return variables for tokenizers
13160 var type, setStyle;
13161
13162 function inText(stream, state) {
13163 function chain(parser) {
13164 state.tokenize = parser;
13165 return parser(stream, state);
13166 }
13167
13168 var ch = stream.next();
13169 if (ch == "<") {
13170 if (stream.eat("!")) {
13171 if (stream.eat("[")) {
13172 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13173 else return null;
13174 } else if (stream.match("--")) {
13175 return chain(inBlock("comment", "-->"));
13176 } else if (stream.match("DOCTYPE", true, true)) {
13177 stream.eatWhile(/[\w\._\-]/);
13178 return chain(doctype(1));
13179 } else {
13180 return null;
13181 }
13182 } else if (stream.eat("?")) {
13183 stream.eatWhile(/[\w\._\-]/);
13184 state.tokenize = inBlock("meta", "?>");
13185 return "meta";
13186 } else {
13187 type = stream.eat("/") ? "closeTag" : "openTag";
13188 state.tokenize = inTag;
13189 return "tag bracket";
13190 }
13191 } else if (ch == "&") {
13192 var ok;
13193 if (stream.eat("#")) {
13194 if (stream.eat("x")) {
13195 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13196 } else {
13197 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13198 }
13199 } else {
13200 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13201 }
13202 return ok ? "atom" : "error";
13203 } else {
13204 stream.eatWhile(/[^&<]/);
13205 return null;
13206 }
13207 }
13208 inText.isInText = true;
13209
13210 function inTag(stream, state) {
13211 var ch = stream.next();
13212 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13213 state.tokenize = inText;
13214 type = ch == ">" ? "endTag" : "selfcloseTag";
13215 return "tag bracket";
13216 } else if (ch == "=") {
13217 type = "equals";
13218 return null;
13219 } else if (ch == "<") {
13220 state.tokenize = inText;
13221 state.state = baseState;
13222 state.tagName = state.tagStart = null;
13223 var next = state.tokenize(stream, state);
13224 return next ? next + " tag error" : "tag error";
13225 } else if (/[\'\"]/.test(ch)) {
13226 state.tokenize = inAttribute(ch);
13227 state.stringStartCol = stream.column();
13228 return state.tokenize(stream, state);
13229 } else {
13230 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13231 return "word";
13232 }
13233 }
13234
13235 function inAttribute(quote) {
13236 var closure = function(stream, state) {
13237 while (!stream.eol()) {
13238 if (stream.next() == quote) {
13239 state.tokenize = inTag;
13240 break;
13241 }
13242 }
13243 return "string";
13244 };
13245 closure.isInAttribute = true;
13246 return closure;
13247 }
13248
13249 function inBlock(style, terminator) {
13250 return function(stream, state) {
13251 while (!stream.eol()) {
13252 if (stream.match(terminator)) {
13253 state.tokenize = inText;
13254 break;
13255 }
13256 stream.next();
13257 }
13258 return style;
13259 };
13260 }
13261 function doctype(depth) {
13262 return function(stream, state) {
13263 var ch;
13264 while ((ch = stream.next()) != null) {
13265 if (ch == "<") {
13266 state.tokenize = doctype(depth + 1);
13267 return state.tokenize(stream, state);
13268 } else if (ch == ">") {
13269 if (depth == 1) {
13270 state.tokenize = inText;
13271 break;
13272 } else {
13273 state.tokenize = doctype(depth - 1);
13274 return state.tokenize(stream, state);
13275 }
13276 }
13277 }
13278 return "meta";
13279 };
13280 }
13281
13282 function Context(state, tagName, startOfLine) {
13283 this.prev = state.context;
13284 this.tagName = tagName;
13285 this.indent = state.indented;
13286 this.startOfLine = startOfLine;
13287 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13288 this.noIndent = true;
13289 }
13290 function popContext(state) {
13291 if (state.context) state.context = state.context.prev;
13292 }
13293 function maybePopContext(state, nextTagName) {
13294 var parentTagName;
13295 while (true) {
13296 if (!state.context) {
13297 return;
13298 }
13299 parentTagName = state.context.tagName;
13300 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13301 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13302 return;
13303 }
13304 popContext(state);
13305 }
13306 }
13307
13308 function baseState(type, stream, state) {
13309 if (type == "openTag") {
13310 state.tagStart = stream.column();
13311 return tagNameState;
13312 } else if (type == "closeTag") {
13313 return closeTagNameState;
13314 } else {
13315 return baseState;
13316 }
13317 }
13318 function tagNameState(type, stream, state) {
13319 if (type == "word") {
13320 state.tagName = stream.current();
13321 setStyle = "tag";
13322 return attrState;
13323 } else if (config.allowMissingTagName && type == "endTag") {
13324 setStyle = "tag bracket";
13325 return attrState(type, stream, state);
13326 } else {
13327 setStyle = "error";
13328 return tagNameState;
13329 }
13330 }
13331 function closeTagNameState(type, stream, state) {
13332 if (type == "word") {
13333 var tagName = stream.current();
13334 if (state.context && state.context.tagName != tagName &&
13335 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13336 popContext(state);
13337 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13338 setStyle = "tag";
13339 return closeState;
13340 } else {
13341 setStyle = "tag error";
13342 return closeStateErr;
13343 }
13344 } else if (config.allowMissingTagName && type == "endTag") {
13345 setStyle = "tag bracket";
13346 return closeState(type, stream, state);
13347 } else {
13348 setStyle = "error";
13349 return closeStateErr;
13350 }
13351 }
13352
13353 function closeState(type, _stream, state) {
13354 if (type != "endTag") {
13355 setStyle = "error";
13356 return closeState;
13357 }
13358 popContext(state);
13359 return baseState;
13360 }
13361 function closeStateErr(type, stream, state) {
13362 setStyle = "error";
13363 return closeState(type, stream, state);
13364 }
13365
13366 function attrState(type, _stream, state) {
13367 if (type == "word") {
13368 setStyle = "attribute";
13369 return attrEqState;
13370 } else if (type == "endTag" || type == "selfcloseTag") {
13371 var tagName = state.tagName, tagStart = state.tagStart;
13372 state.tagName = state.tagStart = null;
13373 if (type == "selfcloseTag" ||
13374 config.autoSelfClosers.hasOwnProperty(tagName)) {
13375 maybePopContext(state, tagName);
13376 } else {
13377 maybePopContext(state, tagName);
13378 state.context = new Context(state, tagName, tagStart == state.indented);
13379 }
13380 return baseState;
13381 }
13382 setStyle = "error";
13383 return attrState;
13384 }
13385 function attrEqState(type, stream, state) {
13386 if (type == "equals") return attrValueState;
13387 if (!config.allowMissing) setStyle = "error";
13388 return attrState(type, stream, state);
13389 }
13390 function attrValueState(type, stream, state) {
13391 if (type == "string") return attrContinuedState;
13392 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13393 setStyle = "error";
13394 return attrState(type, stream, state);
13395 }
13396 function attrContinuedState(type, stream, state) {
13397 if (type == "string") return attrContinuedState;
13398 return attrState(type, stream, state);
13399 }
13400
13401 return {
13402 startState: function(baseIndent) {
13403 var state = {tokenize: inText,
13404 state: baseState,
13405 indented: baseIndent || 0,
13406 tagName: null, tagStart: null,
13407 context: null}
13408 if (baseIndent != null) state.baseIndent = baseIndent
13409 return state
13410 },
13411
13412 token: function(stream, state) {
13413 if (!state.tagName && stream.sol())
13414 state.indented = stream.indentation();
13415
13416 if (stream.eatSpace()) return null;
13417 type = null;
13418 var style = state.tokenize(stream, state);
13419 if ((style || type) && style != "comment") {
13420 setStyle = null;
13421 state.state = state.state(type || style, stream, state);
13422 if (setStyle)
13423 style = setStyle == "error" ? style + " error" : setStyle;
13424 }
13425 return style;
13426 },
13427
13428 indent: function(state, textAfter, fullLine) {
13429 var context = state.context;
13430 // Indent multi-line strings (e.g. css).
13431 if (state.tokenize.isInAttribute) {
13432 if (state.tagStart == state.indented)
13433 return state.stringStartCol + 1;
13434 else
13435 return state.indented + indentUnit;
13436 }
13437 if (context && context.noIndent) return CodeMirror.Pass;
13438 if (state.tokenize != inTag && state.tokenize != inText)
13439 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13440 // Indent the starts of attribute names.
13441 if (state.tagName) {
13442 if (config.multilineTagIndentPastTag !== false)
13443 return state.tagStart + state.tagName.length + 2;
13444 else
13445 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13446 }
13447 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13448 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13449 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13450 while (context) {
13451 if (context.tagName == tagAfter[2]) {
13452 context = context.prev;
13453 break;
13454 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13455 context = context.prev;
13456 } else {
13457 break;
13458 }
13459 }
13460 } else if (tagAfter) { // Opening tag spotted
13461 while (context) {
13462 var grabbers = config.contextGrabbers[context.tagName];
13463 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13464 context = context.prev;
13465 else
13466 break;
13467 }
13468 }
13469 while (context && context.prev && !context.startOfLine)
13470 context = context.prev;
13471 if (context) return context.indent + indentUnit;
13472 else return state.baseIndent || 0;
13473 },
13474
13475 electricInput: /<\/[\s\w:]+>$/,
13476 blockCommentStart: "<!--",
13477 blockCommentEnd: "-->",
13478
13479 configuration: config.htmlMode ? "html" : "xml",
13480 helperType: config.htmlMode ? "html" : "xml",
13481
13482 skipAttribute: function(state) {
13483 if (state.state == attrValueState)
13484 state.state = attrState
13485 }
13486 };
13487 });
13488
13489 CodeMirror.defineMIME("text/xml", "xml");
13490 CodeMirror.defineMIME("application/xml", "xml");
13491 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13492 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13493
13494 });
13495
13496 },{"../../lib/codemirror":14}],19:[function(require,module,exports){
13497 (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);};};}});
13498
13499
13500 },{}],20:[function(require,module,exports){
13501 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;
13502
13503
13504 },{}],21:[function(require,module,exports){
13505 (function (global, factory) {
13506 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13507 typeof define === 'function' && define.amd ? define(factory) :
13508 (global.preactRenderToString = factory());
13509 }(this, (function () {
13510
13511 var NON_DIMENSION_PROPS = {
13512 boxFlex: 1, boxFlexGroup: 1, columnCount: 1, fillOpacity: 1, flex: 1, flexGrow: 1,
13513 flexPositive: 1, flexShrink: 1, flexNegative: 1, fontWeight: 1, lineClamp: 1, lineHeight: 1,
13514 opacity: 1, order: 1, orphans: 1, strokeOpacity: 1, widows: 1, zIndex: 1, zoom: 1
13515 };
13516
13517 var ESC = {
13518 '<': '&lt;',
13519 '>': '&gt;',
13520 '"': '&quot;',
13521 '&': '&amp;'
13522 };
13523
13524 var objectKeys = Object.keys || function (obj) {
13525 var keys = [];
13526 for (var i in obj) {
13527 if (obj.hasOwnProperty(i)) keys.push(i);
13528 }return keys;
13529 };
13530
13531 var encodeEntities = function encodeEntities(s) {
13532 return String(s).replace(/[<>"&]/g, escapeChar);
13533 };
13534
13535 var escapeChar = function escapeChar(a) {
13536 return ESC[a] || a;
13537 };
13538
13539 var falsey = function falsey(v) {
13540 return v == null || v === false;
13541 };
13542
13543 var memoize = function memoize(fn) {
13544 var mem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13545 return function (v) {
13546 return mem[v] || (mem[v] = fn(v));
13547 };
13548 };
13549
13550 var indent = function indent(s, char) {
13551 return String(s).replace(/(\n+)/g, '$1' + (char || '\t'));
13552 };
13553
13554 var isLargeString = function isLargeString(s, length, ignoreLines) {
13555 return String(s).length > (length || 40) || !ignoreLines && String(s).indexOf('\n') !== -1 || String(s).indexOf('<') !== -1;
13556 };
13557
13558 function styleObjToCss(s) {
13559 var str = '';
13560 for (var prop in s) {
13561 var val = s[prop];
13562 if (val != null) {
13563 if (str) str += ' ';
13564 str += jsToCss(prop);
13565 str += ': ';
13566 str += val;
13567 if (typeof val === 'number' && !NON_DIMENSION_PROPS[prop]) {
13568 str += 'px';
13569 }
13570 str += ';';
13571 }
13572 }
13573 return str || undefined;
13574 }
13575
13576 function hashToClassName(c) {
13577 var str = '';
13578 for (var prop in c) {
13579 if (c[prop]) {
13580 if (str) str += ' ';
13581 str += prop;
13582 }
13583 }
13584 return str;
13585 }
13586
13587 var jsToCss = memoize(function (s) {
13588 return s.replace(/([A-Z])/g, '-$1').toLowerCase();
13589 });
13590
13591 function assign(obj, props) {
13592 for (var i in props) {
13593 obj[i] = props[i];
13594 }return obj;
13595 }
13596
13597 function getNodeProps(vnode) {
13598 var defaultProps = vnode.nodeName.defaultProps,
13599 props = assign({}, defaultProps || vnode.attributes);
13600 if (defaultProps) assign(props, vnode.attributes);
13601 if (vnode.children) props.children = vnode.children;
13602 return props;
13603 }
13604
13605 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
13606
13607 var SHALLOW = { shallow: true };
13608
13609 var UNNAMED = [];
13610
13611 var EMPTY = {};
13612
13613 var VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
13614
13615 renderToString.render = renderToString;
13616
13617 var shallowRender = function shallowRender(vnode, context) {
13618 return renderToString(vnode, context, SHALLOW);
13619 };
13620
13621 function renderToString(vnode, context, opts, inner, isSvgMode) {
13622 var _ref = vnode || EMPTY,
13623 nodeName = _ref.nodeName,
13624 attributes = _ref.attributes,
13625 children = _ref.children,
13626 isComponent = false;
13627
13628 context = context || {};
13629 opts = opts || {};
13630
13631 var pretty = opts.pretty,
13632 indentChar = typeof pretty === 'string' ? pretty : '\t';
13633
13634 if (vnode == null || typeof vnode === 'boolean') {
13635 return '';
13636 }
13637
13638 if ((typeof vnode === 'undefined' ? 'undefined' : _typeof(vnode)) !== 'object' && !nodeName) {
13639 return encodeEntities(vnode);
13640 }
13641
13642 if (typeof nodeName === 'function') {
13643 isComponent = true;
13644 if (opts.shallow && (inner || opts.renderRootComponent === false)) {
13645 nodeName = getComponentName(nodeName);
13646 } else {
13647 var props = getNodeProps(vnode),
13648 rendered = void 0;
13649
13650 if (!nodeName.prototype || typeof nodeName.prototype.render !== 'function') {
13651 rendered = nodeName(props, context);
13652 } else {
13653 var c = new nodeName(props, context);
13654
13655 c._disable = c.__x = true;
13656 c.props = props;
13657 c.context = context;
13658 if (c.componentWillMount) c.componentWillMount();
13659 rendered = c.render(c.props, c.state, c.context);
13660
13661 if (c.getChildContext) {
13662 context = assign(assign({}, context), c.getChildContext());
13663 }
13664 }
13665
13666 return renderToString(rendered, context, opts, opts.shallowHighOrder !== false);
13667 }
13668 }
13669
13670 var s = '',
13671 html = void 0;
13672
13673 if (attributes) {
13674 var attrs = objectKeys(attributes);
13675
13676 if (opts && opts.sortAttributes === true) attrs.sort();
13677
13678 for (var i = 0; i < attrs.length; i++) {
13679 var name = attrs[i],
13680 v = attributes[name];
13681 if (name === 'children') continue;
13682 if (!(opts && opts.allAttributes) && (name === 'key' || name === 'ref')) continue;
13683
13684 if (name === 'className') {
13685 if (attributes['class']) continue;
13686 name = 'class';
13687 } else if (isSvgMode && name.match(/^xlink\:?(.+)/)) {
13688 name = name.toLowerCase().replace(/^xlink\:?(.+)/, 'xlink:$1');
13689 }
13690
13691 if (name === 'class' && v && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object') {
13692 v = hashToClassName(v);
13693 } else if (name === 'style' && v && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object') {
13694 v = styleObjToCss(v);
13695 }
13696
13697 var hooked = opts.attributeHook && opts.attributeHook(name, v, context, opts, isComponent);
13698 if (hooked || hooked === '') {
13699 s += hooked;
13700 continue;
13701 }
13702
13703 if (name === 'dangerouslySetInnerHTML') {
13704 html = v && v.__html;
13705 } else if ((v || v === 0 || v === '') && typeof v !== 'function') {
13706 if (v === true || v === '') {
13707 v = name;
13708
13709 if (!opts || !opts.xml) {
13710 s += ' ' + name;
13711 continue;
13712 }
13713 }
13714 s += ' ' + name + '="' + encodeEntities(v) + '"';
13715 }
13716 }
13717 }
13718
13719 var sub = s.replace(/^\n\s*/, ' ');
13720 if (sub !== s && !~sub.indexOf('\n')) s = sub;else if (pretty && ~s.indexOf('\n')) s += '\n';
13721
13722 s = '<' + nodeName + s + '>';
13723
13724 if (VOID_ELEMENTS.indexOf(nodeName) > -1) {
13725 s = s.replace(/>$/, ' />');
13726 }
13727
13728 if (html) {
13729 if (pretty && isLargeString(html)) {
13730 html = '\n' + indentChar + indent(html, indentChar);
13731 }
13732 s += html;
13733 } else {
13734 var len = children && children.length,
13735 pieces = [],
13736 hasLarge = ~s.indexOf('\n');
13737 for (var _i = 0; _i < len; _i++) {
13738 var child = children[_i];
13739 if (!falsey(child)) {
13740 var childSvgMode = nodeName === 'svg' ? true : nodeName === 'foreignObject' ? false : isSvgMode,
13741 ret = renderToString(child, context, opts, true, childSvgMode);
13742 if (!hasLarge && pretty && isLargeString(ret)) hasLarge = true;
13743 if (ret) pieces.push(ret);
13744 }
13745 }
13746 if (pretty && hasLarge) {
13747 for (var _i2 = pieces.length; _i2--;) {
13748 pieces[_i2] = '\n' + indentChar + indent(pieces[_i2], indentChar);
13749 }
13750 }
13751 if (pieces.length) {
13752 s += pieces.join('');
13753 } else if (opts && opts.xml) {
13754 return s.substring(0, s.length - 1) + ' />';
13755 }
13756 }
13757
13758 if (VOID_ELEMENTS.indexOf(nodeName) === -1) {
13759 if (pretty && ~s.indexOf('\n')) s += '\n';
13760 s += '</' + nodeName + '>';
13761 }
13762
13763 return s;
13764 }
13765
13766 function getComponentName(component) {
13767 return component.displayName || component !== Function && component.name || getFallbackComponentName(component);
13768 }
13769
13770 function getFallbackComponentName(component) {
13771 var str = Function.prototype.toString.call(component),
13772 name = (str.match(/^\s*function\s+([^\( ]+)/) || EMPTY)[1];
13773 if (!name) {
13774 var index = -1;
13775 for (var i = UNNAMED.length; i--;) {
13776 if (UNNAMED[i] === component) {
13777 index = i;
13778 break;
13779 }
13780 }
13781
13782 if (index < 0) {
13783 index = UNNAMED.push(component) - 1;
13784 }
13785 name = 'UnnamedComponent' + index;
13786 }
13787 return name;
13788 }
13789 renderToString.shallowRender = shallowRender;
13790
13791 return renderToString;
13792
13793 })));
13794
13795
13796 },{}],22:[function(require,module,exports){
13797 !function() {
13798 'use strict';
13799 function VNode() {}
13800 function h(nodeName, attributes) {
13801 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13802 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13803 if (attributes && null != attributes.children) {
13804 if (!stack.length) stack.push(attributes.children);
13805 delete attributes.children;
13806 }
13807 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13808 if ('boolean' == typeof child) child = null;
13809 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13810 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13811 lastSimple = simple;
13812 }
13813 var p = new VNode();
13814 p.nodeName = nodeName;
13815 p.children = children;
13816 p.attributes = null == attributes ? void 0 : attributes;
13817 p.key = null == attributes ? void 0 : attributes.key;
13818 if (void 0 !== options.vnode) options.vnode(p);
13819 return p;
13820 }
13821 function extend(obj, props) {
13822 for (var i in props) obj[i] = props[i];
13823 return obj;
13824 }
13825 function cloneElement(vnode, props) {
13826 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13827 }
13828 function enqueueRender(component) {
13829 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13830 }
13831 function rerender() {
13832 var p, list = items;
13833 items = [];
13834 while (p = list.pop()) if (p.__d) renderComponent(p);
13835 }
13836 function isSameNodeType(node, vnode, hydrating) {
13837 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13838 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13839 }
13840 function isNamedNode(node, nodeName) {
13841 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
13842 }
13843 function getNodeProps(vnode) {
13844 var props = extend({}, vnode.attributes);
13845 props.children = vnode.children;
13846 var defaultProps = vnode.nodeName.defaultProps;
13847 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
13848 return props;
13849 }
13850 function createNode(nodeName, isSvg) {
13851 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
13852 node.__n = nodeName;
13853 return node;
13854 }
13855 function removeNode(node) {
13856 var parentNode = node.parentNode;
13857 if (parentNode) parentNode.removeChild(node);
13858 }
13859 function setAccessor(node, name, old, value, isSvg) {
13860 if ('className' === name) name = 'class';
13861 if ('key' === name) ; else if ('ref' === name) {
13862 if (old) old(null);
13863 if (value) value(node);
13864 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
13865 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
13866 if (value && 'object' == typeof value) {
13867 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
13868 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
13869 }
13870 } else if ('dangerouslySetInnerHTML' === name) {
13871 if (value) node.innerHTML = value.__html || '';
13872 } else if ('o' == name[0] && 'n' == name[1]) {
13873 var useCapture = name !== (name = name.replace(/Capture$/, ''));
13874 name = name.toLowerCase().substring(2);
13875 if (value) {
13876 if (!old) node.addEventListener(name, eventProxy, useCapture);
13877 } else node.removeEventListener(name, eventProxy, useCapture);
13878 (node.__l || (node.__l = {}))[name] = value;
13879 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
13880 setProperty(node, name, null == value ? '' : value);
13881 if (null == value || !1 === value) node.removeAttribute(name);
13882 } else {
13883 var ns = isSvg && name !== (name = name.replace(/^xlink\:?/, ''));
13884 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);
13885 }
13886 }
13887 function setProperty(node, name, value) {
13888 try {
13889 node[name] = value;
13890 } catch (e) {}
13891 }
13892 function eventProxy(e) {
13893 return this.__l[e.type](options.event && options.event(e) || e);
13894 }
13895 function flushMounts() {
13896 var c;
13897 while (c = mounts.pop()) {
13898 if (options.afterMount) options.afterMount(c);
13899 if (c.componentDidMount) c.componentDidMount();
13900 }
13901 }
13902 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
13903 if (!diffLevel++) {
13904 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
13905 hydrating = null != dom && !('__preactattr_' in dom);
13906 }
13907 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
13908 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
13909 if (!--diffLevel) {
13910 hydrating = !1;
13911 if (!componentRoot) flushMounts();
13912 }
13913 return ret;
13914 }
13915 function idiff(dom, vnode, context, mountAll, componentRoot) {
13916 var out = dom, prevSvgMode = isSvgMode;
13917 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
13918 if ('string' == typeof vnode || 'number' == typeof vnode) {
13919 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
13920 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
13921 } else {
13922 out = document.createTextNode(vnode);
13923 if (dom) {
13924 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13925 recollectNodeTree(dom, !0);
13926 }
13927 }
13928 out.__preactattr_ = !0;
13929 return out;
13930 }
13931 var vnodeName = vnode.nodeName;
13932 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
13933 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
13934 vnodeName = String(vnodeName);
13935 if (!dom || !isNamedNode(dom, vnodeName)) {
13936 out = createNode(vnodeName, isSvgMode);
13937 if (dom) {
13938 while (dom.firstChild) out.appendChild(dom.firstChild);
13939 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13940 recollectNodeTree(dom, !0);
13941 }
13942 }
13943 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
13944 if (null == props) {
13945 props = out.__preactattr_ = {};
13946 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
13947 }
13948 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
13949 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
13950 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
13951 diffAttributes(out, vnode.attributes, props);
13952 isSvgMode = prevSvgMode;
13953 return out;
13954 }
13955 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
13956 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;
13957 if (0 !== len) for (var i = 0; i < len; i++) {
13958 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
13959 if (null != key) {
13960 keyedLen++;
13961 keyed[key] = _child;
13962 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
13963 }
13964 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
13965 vchild = vchildren[i];
13966 child = null;
13967 var key = vchild.key;
13968 if (null != key) {
13969 if (keyedLen && void 0 !== keyed[key]) {
13970 child = keyed[key];
13971 keyed[key] = void 0;
13972 keyedLen--;
13973 }
13974 } else if (!child && min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
13975 child = c;
13976 children[j] = void 0;
13977 if (j === childrenLen - 1) childrenLen--;
13978 if (j === min) min++;
13979 break;
13980 }
13981 child = idiff(child, vchild, context, mountAll);
13982 f = originalChildren[i];
13983 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
13984 }
13985 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
13986 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
13987 }
13988 function recollectNodeTree(node, unmountOnly) {
13989 var component = node._component;
13990 if (component) unmountComponent(component); else {
13991 if (null != node.__preactattr_ && node.__preactattr_.ref) node.__preactattr_.ref(null);
13992 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
13993 removeChildren(node);
13994 }
13995 }
13996 function removeChildren(node) {
13997 node = node.lastChild;
13998 while (node) {
13999 var next = node.previousSibling;
14000 recollectNodeTree(node, !0);
14001 node = next;
14002 }
14003 }
14004 function diffAttributes(dom, attrs, old) {
14005 var name;
14006 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
14007 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);
14008 }
14009 function collectComponent(component) {
14010 var name = component.constructor.name;
14011 (components[name] || (components[name] = [])).push(component);
14012 }
14013 function createComponent(Ctor, props, context) {
14014 var inst, list = components[Ctor.name];
14015 if (Ctor.prototype && Ctor.prototype.render) {
14016 inst = new Ctor(props, context);
14017 Component.call(inst, props, context);
14018 } else {
14019 inst = new Component(props, context);
14020 inst.constructor = Ctor;
14021 inst.render = doRender;
14022 }
14023 if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) {
14024 inst.__b = list[i].__b;
14025 list.splice(i, 1);
14026 break;
14027 }
14028 return inst;
14029 }
14030 function doRender(props, state, context) {
14031 return this.constructor(props, context);
14032 }
14033 function setComponentProps(component, props, opts, context, mountAll) {
14034 if (!component.__x) {
14035 component.__x = !0;
14036 if (component.__r = props.ref) delete props.ref;
14037 if (component.__k = props.key) delete props.key;
14038 if (!component.base || mountAll) {
14039 if (component.componentWillMount) component.componentWillMount();
14040 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
14041 if (context && context !== component.context) {
14042 if (!component.__c) component.__c = component.context;
14043 component.context = context;
14044 }
14045 if (!component.__p) component.__p = component.props;
14046 component.props = props;
14047 component.__x = !1;
14048 if (0 !== opts) if (1 === opts || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
14049 if (component.__r) component.__r(component);
14050 }
14051 }
14052 function renderComponent(component, opts, mountAll, isChild) {
14053 if (!component.__x) {
14054 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;
14055 if (isUpdate) {
14056 component.props = previousProps;
14057 component.state = previousState;
14058 component.context = previousContext;
14059 if (2 !== opts && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
14060 component.props = props;
14061 component.state = state;
14062 component.context = context;
14063 }
14064 component.__p = component.__s = component.__c = component.__b = null;
14065 component.__d = !1;
14066 if (!skip) {
14067 rendered = component.render(props, state, context);
14068 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
14069 var toUnmount, base, childComponent = rendered && rendered.nodeName;
14070 if ('function' == typeof childComponent) {
14071 var childProps = getNodeProps(rendered);
14072 inst = initialChildComponent;
14073 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
14074 toUnmount = inst;
14075 component._component = inst = createComponent(childComponent, childProps, context);
14076 inst.__b = inst.__b || nextBase;
14077 inst.__u = component;
14078 setComponentProps(inst, childProps, 0, context, !1);
14079 renderComponent(inst, 1, mountAll, !0);
14080 }
14081 base = inst.base;
14082 } else {
14083 cbase = initialBase;
14084 toUnmount = initialChildComponent;
14085 if (toUnmount) cbase = component._component = null;
14086 if (initialBase || 1 === opts) {
14087 if (cbase) cbase._component = null;
14088 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
14089 }
14090 }
14091 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
14092 var baseParent = initialBase.parentNode;
14093 if (baseParent && base !== baseParent) {
14094 baseParent.replaceChild(base, initialBase);
14095 if (!toUnmount) {
14096 initialBase._component = null;
14097 recollectNodeTree(initialBase, !1);
14098 }
14099 }
14100 }
14101 if (toUnmount) unmountComponent(toUnmount);
14102 component.base = base;
14103 if (base && !isChild) {
14104 var componentRef = component, t = component;
14105 while (t = t.__u) (componentRef = t).base = base;
14106 base._component = componentRef;
14107 base._componentConstructor = componentRef.constructor;
14108 }
14109 }
14110 if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) {
14111 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext);
14112 if (options.afterUpdate) options.afterUpdate(component);
14113 }
14114 if (null != component.__h) while (component.__h.length) component.__h.pop().call(component);
14115 if (!diffLevel && !isChild) flushMounts();
14116 }
14117 }
14118 function buildComponentFromVNode(dom, vnode, context, mountAll) {
14119 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
14120 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
14121 if (c && isOwner && (!mountAll || c._component)) {
14122 setComponentProps(c, props, 3, context, mountAll);
14123 dom = c.base;
14124 } else {
14125 if (originalComponent && !isDirectOwner) {
14126 unmountComponent(originalComponent);
14127 dom = oldDom = null;
14128 }
14129 c = createComponent(vnode.nodeName, props, context);
14130 if (dom && !c.__b) {
14131 c.__b = dom;
14132 oldDom = null;
14133 }
14134 setComponentProps(c, props, 1, context, mountAll);
14135 dom = c.base;
14136 if (oldDom && dom !== oldDom) {
14137 oldDom._component = null;
14138 recollectNodeTree(oldDom, !1);
14139 }
14140 }
14141 return dom;
14142 }
14143 function unmountComponent(component) {
14144 if (options.beforeUnmount) options.beforeUnmount(component);
14145 var base = component.base;
14146 component.__x = !0;
14147 if (component.componentWillUnmount) component.componentWillUnmount();
14148 component.base = null;
14149 var inner = component._component;
14150 if (inner) unmountComponent(inner); else if (base) {
14151 if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null);
14152 component.__b = base;
14153 removeNode(base);
14154 collectComponent(component);
14155 removeChildren(base);
14156 }
14157 if (component.__r) component.__r(null);
14158 }
14159 function Component(props, context) {
14160 this.__d = !0;
14161 this.context = context;
14162 this.props = props;
14163 this.state = this.state || {};
14164 }
14165 function render(vnode, parent, merge) {
14166 return diff(merge, vnode, {}, !1, parent, !1);
14167 }
14168 var options = {};
14169 var stack = [];
14170 var EMPTY_CHILDREN = [];
14171 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14172 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14173 var items = [];
14174 var mounts = [];
14175 var diffLevel = 0;
14176 var isSvgMode = !1;
14177 var hydrating = !1;
14178 var components = {};
14179 extend(Component.prototype, {
14180 setState: function(state, callback) {
14181 var s = this.state;
14182 if (!this.__s) this.__s = extend({}, s);
14183 extend(s, 'function' == typeof state ? state(s, this.props) : state);
14184 if (callback) (this.__h = this.__h || []).push(callback);
14185 enqueueRender(this);
14186 },
14187 forceUpdate: function(callback) {
14188 if (callback) (this.__h = this.__h || []).push(callback);
14189 renderComponent(this, 2);
14190 },
14191 render: function() {}
14192 });
14193 var preact = {
14194 h: h,
14195 createElement: h,
14196 cloneElement: cloneElement,
14197 Component: Component,
14198 render: render,
14199 rerender: rerender,
14200 options: options
14201 };
14202 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14203 }();
14204
14205 },{}],23:[function(require,module,exports){
14206 function tlite(getTooltipOpts) {
14207 document.addEventListener('mouseover', function (e) {
14208 var el = e.target;
14209 var opts = getTooltipOpts(el);
14210
14211 if (!opts) {
14212 el = el.parentElement;
14213 opts = el && getTooltipOpts(el);
14214 }
14215
14216 opts && tlite.show(el, opts, true);
14217 });
14218 }
14219
14220 tlite.show = function (el, opts, isAuto) {
14221 var fallbackAttrib = 'data-tlite';
14222 opts = opts || {};
14223
14224 (el.tooltip || Tooltip(el, opts)).show();
14225
14226 function Tooltip(el, opts) {
14227 var tooltipEl;
14228 var showTimer;
14229 var text;
14230
14231 el.addEventListener('mousedown', autoHide);
14232 el.addEventListener('mouseleave', autoHide);
14233
14234 function show() {
14235 text = el.title || el.getAttribute(fallbackAttrib) || text;
14236 el.title = '';
14237 el.setAttribute(fallbackAttrib, '');
14238 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
14239 }
14240
14241 function autoHide() {
14242 tlite.hide(el, true);
14243 }
14244
14245 function hide(isAutoHiding) {
14246 if (isAuto === isAutoHiding) {
14247 showTimer = clearTimeout(showTimer);
14248 var parent = tooltipEl && tooltipEl.parentNode;
14249 parent && parent.removeChild(tooltipEl);
14250 tooltipEl = undefined;
14251 }
14252 }
14253
14254 function fadeIn() {
14255 if (!tooltipEl) {
14256 tooltipEl = createTooltip(el, text, opts);
14257 }
14258 }
14259
14260 return el.tooltip = {
14261 show: show,
14262 hide: hide
14263 };
14264 }
14265
14266 function createTooltip(el, text, opts) {
14267 var tooltipEl = document.createElement('span');
14268 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
14269
14270 tooltipEl.innerHTML = text;
14271
14272 el.appendChild(tooltipEl);
14273
14274 var vertGrav = grav[0] || '';
14275 var horzGrav = grav[1] || '';
14276
14277 function positionTooltip() {
14278 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
14279
14280 var arrowSize = 10;
14281 var top = el.offsetTop;
14282 var left = el.offsetLeft;
14283
14284 if (tooltipEl.offsetParent === el) {
14285 top = left = 0;
14286 }
14287
14288 var width = el.offsetWidth;
14289 var height = el.offsetHeight;
14290 var tooltipHeight = tooltipEl.offsetHeight;
14291 var tooltipWidth = tooltipEl.offsetWidth;
14292 var centerEl = left + (width / 2);
14293
14294 tooltipEl.style.top = (
14295 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
14296 vertGrav === 'n' ? (top + height + arrowSize) :
14297 (top + (height / 2) - (tooltipHeight / 2))
14298 ) + 'px';
14299
14300 tooltipEl.style.left = (
14301 horzGrav === 'w' ? left :
14302 horzGrav === 'e' ? left + width - tooltipWidth :
14303 vertGrav === 'w' ? (left + width + arrowSize) :
14304 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
14305 (centerEl - tooltipWidth / 2)
14306 ) + 'px';
14307 }
14308
14309 positionTooltip();
14310
14311 var rect = tooltipEl.getBoundingClientRect();
14312
14313 if (vertGrav === 's' && rect.top < 0) {
14314 vertGrav = 'n';
14315 positionTooltip();
14316 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
14317 vertGrav = 's';
14318 positionTooltip();
14319 } else if (vertGrav === 'e' && rect.left < 0) {
14320 vertGrav = 'w';
14321 positionTooltip();
14322 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
14323 vertGrav = 'e';
14324 positionTooltip();
14325 }
14326
14327 tooltipEl.className += ' tlite-visible';
14328
14329 return tooltipEl;
14330 }
14331 };
14332
14333 tlite.hide = function (el, isAuto) {
14334 el.tooltip && el.tooltip.hide(isAuto);
14335 };
14336
14337 if (typeof module !== 'undefined' && module.exports) {
14338 module.exports = tlite;
14339 }
14340
14341 },{}]},{},[2]);
14342 ; })();