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

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

14,366 lines 544.6 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 // tell WP common.js to override the method used for determining hidden columns (screen options)
78 if (hf_options.view === 'edit') {
79 window.columns.useCheckboxesForHidden();
80 }
81
82 },{"./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){
83 'use strict';
84
85 Object.defineProperty(exports, "__esModule", {
86 value: true
87 });
88 exports.FieldBuilder = undefined;
89
90 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; }; }();
91
92 var _desc, _value, _class;
93
94 var _preact = require('preact');
95
96 var _decko = require('decko');
97
98 var _fieldConfigurator = require('./field-configurator.js');
99
100 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
101
102 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; }
103
104 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; }
105
106 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
107 var desc = {};
108 Object['ke' + 'ys'](descriptor).forEach(function (key) {
109 desc[key] = descriptor[key];
110 });
111 desc.enumerable = !!desc.enumerable;
112 desc.configurable = !!desc.configurable;
113
114 if ('value' in desc || desc.initializer) {
115 desc.writable = true;
116 }
117
118 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
119 return decorator(target, property, desc) || desc;
120 }, desc);
121
122 if (context && desc.initializer !== void 0) {
123 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
124 desc.initializer = undefined;
125 }
126
127 if (desc.initializer === void 0) {
128 Object['define' + 'Property'](target, property, desc);
129 desc = null;
130 }
131
132 return desc;
133 }
134
135 var FieldBuilder = (_class = function (_Component) {
136 _inherits(FieldBuilder, _Component);
137
138 function FieldBuilder(props) {
139 _classCallCheck(this, FieldBuilder);
140
141 var _this = _possibleConstructorReturn(this, (FieldBuilder.__proto__ || Object.getPrototypeOf(FieldBuilder)).call(this, props));
142
143 _this.state = {
144 activeField: null
145 };
146 return _this;
147 }
148
149 _createClass(FieldBuilder, [{
150 key: 'handleCancel',
151 value: function handleCancel() {
152 this.setState({
153 activeField: null
154 });
155 }
156 }, {
157 key: 'openFieldConfig',
158 value: function openFieldConfig(e) {
159 var field = this.props.fields[e.target.value];
160
161 if (this.state.activeField === field) {
162 this.setState({ activeField: null });
163 } else {
164 this.setState({ activeField: field });
165 }
166 }
167 }, {
168 key: 'render',
169 value: function render(props, state) {
170 var _this2 = this;
171
172 var fieldButtons = props.fields.map(function (f, i) {
173 return (0, _preact.h)(
174 'button',
175 { type: 'button', value: i, className: "button " + (state.activeField === f ? "active" : ""), onClick: _this2.openFieldConfig },
176 f.label
177 );
178 });
179 var fieldType = state.activeField ? state.activeField.key : "";
180 var rows = state.activeField ? state.activeField.configRows : [];
181
182 return (0, _preact.h)(
183 'div',
184 { 'class': 'hf-field-builder' },
185 (0, _preact.h)(
186 'h4',
187 null,
188 'Add field'
189 ),
190 (0, _preact.h)(
191 'div',
192 { 'class': 'available-fields' },
193 fieldButtons
194 ),
195 (0, _preact.h)(
196 'div',
197 { style: 'max-width: 480px;' },
198 (0, _preact.h)(_fieldConfigurator.FieldConfigurator, { fieldType: fieldType, rows: rows, onCancel: this.handleCancel })
199 ),
200 state.activeField === null ? (0, _preact.h)(
201 'p',
202 { 'class': 'help', style: 'margin-bottom: 0;' },
203 'Use the buttons above to generate your field HTML, or manually modify your form below.'
204 ) : ""
205 );
206 }
207 }]);
208
209 return FieldBuilder;
210 }(_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);
211 exports.FieldBuilder = FieldBuilder;
212
213 },{"./field-configurator.js":4,"decko":19,"preact":22}],4:[function(require,module,exports){
214 'use strict';
215
216 Object.defineProperty(exports, "__esModule", {
217 value: true
218 });
219 exports.FieldConfigurator = undefined;
220
221 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; }; }();
222
223 var _desc, _value, _class;
224
225 var _preact = require('preact');
226
227 var _decko = require('decko');
228
229 var _html = require('../field-builder/html.js');
230
231 var _fieldSettings = require('./field-settings.js');
232
233 var FS = _interopRequireWildcard(_fieldSettings);
234
235 var _linkstate = require('linkstate');
236
237 var _linkstate2 = _interopRequireDefault(_linkstate);
238
239 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
240
241 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; } }
242
243 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
244
245 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; }
246
247 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; }
248
249 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
250 var desc = {};
251 Object['ke' + 'ys'](descriptor).forEach(function (key) {
252 desc[key] = descriptor[key];
253 });
254 desc.enumerable = !!desc.enumerable;
255 desc.configurable = !!desc.configurable;
256
257 if ('value' in desc || desc.initializer) {
258 desc.writable = true;
259 }
260
261 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
262 return decorator(target, property, desc) || desc;
263 }, desc);
264
265 if (context && desc.initializer !== void 0) {
266 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
267 desc.initializer = undefined;
268 }
269
270 if (desc.initializer === void 0) {
271 Object['define' + 'Property'](target, property, desc);
272 desc = null;
273 }
274
275 return desc;
276 }
277
278 var FieldConfigurator = (_class = function (_Component) {
279 _inherits(FieldConfigurator, _Component);
280
281 function FieldConfigurator(props) {
282 _classCallCheck(this, FieldConfigurator);
283
284 var _this = _possibleConstructorReturn(this, (FieldConfigurator.__proto__ || Object.getPrototypeOf(FieldConfigurator)).call(this, props));
285
286 _this.state = _this.getInitialState();
287 _this.choiceHandlers = {
288 "add": _this.addChoice,
289 "delete": _this.deleteChoice,
290 "changeLabel": _this.changeChoiceLabel,
291 "toggleChecked": _this.toggleChoiceChecked
292 };
293 return _this;
294 }
295
296 _createClass(FieldConfigurator, [{
297 key: 'getInitialState',
298 value: function getInitialState() {
299 return {
300 formId: document.querySelector('input[name="form_id"]').value,
301 formSlug: document.querySelector('input[name="form[slug]"]').value,
302 fieldType: "",
303 fieldLabel: "",
304 placeholder: "",
305 value: "",
306 wrap: true,
307 required: false,
308 choices: [{
309 checked: false,
310 label: "One"
311 }, {
312 checked: false,
313 label: "Two"
314 }],
315 accept: ''
316 };
317 }
318 }, {
319 key: 'componentWillReceiveProps',
320 value: function componentWillReceiveProps(props) {
321 var newState = { fieldType: props.fieldType };
322
323 // when changing from field that accepts multiple values to single-value field, reset all pre-selections
324 if (this.state.fieldType === 'checkbox' && props.fieldType !== 'checkbox') {
325 newState.choices = this.state.choices.map(function (c, i) {
326 c.checked = false;
327 return c;
328 });
329 }
330 this.setState(newState);
331 }
332 }, {
333 key: 'addToForm',
334 value: function addToForm() {
335 var html = (0, _html.htmlgenerate)(this.state);
336 html_forms.Editor.replaceSelection(html);
337 }
338 }, {
339 key: 'addChoice',
340 value: function addChoice() {
341 var arr = this.state.choices;
342 arr.push({ checked: false, label: "..." });
343 this.setState({ choices: arr });
344 }
345 }, {
346 key: 'deleteChoice',
347 value: function deleteChoice(e) {
348 var arr = this.state.choices;
349 var index = e.target.parentElement.getAttribute('data-key');
350 arr.splice(index, 1);
351 this.setState({ choices: arr });
352 }
353 }, {
354 key: 'changeChoiceLabel',
355 value: function changeChoiceLabel(e) {
356 var arr = this.state.choices;
357 var index = e.target.parentElement.getAttribute('data-key');
358 arr[index].label = e.target.value;
359 this.setState({ choices: arr });
360 }
361 }, {
362 key: 'toggleChoiceChecked',
363 value: function toggleChoiceChecked(e) {
364 var arr = this.state.choices;
365 var index = e.target.parentElement.getAttribute('data-key');
366 arr[index].checked = !arr[index].checked;
367 this.setState({ choices: arr });
368 }
369 }, {
370 key: 'handleCancel',
371 value: function handleCancel() {
372 // revert back to initial state
373 this.setState(this.getInitialState());
374 this.props.onCancel();
375 }
376 }, {
377 key: 'render',
378 value: function render(props, state) {
379 if (props.rows.length == 0) {
380 return "";
381 }
382
383 var formFields = [];
384
385 for (var i = 0; i < props.rows.length; i++) {
386 switch (props.rows[i]) {
387 case "label":
388 formFields.push((0, _preact.h)(FS.Label, { value: state.fieldLabel, onChange: (0, _linkstate2.default)(this, 'fieldLabel') }));
389 break;
390
391 case "placeholder":
392 formFields.push((0, _preact.h)(FS.Placeholder, { value: state.placeholder, onChange: (0, _linkstate2.default)(this, 'placeholder') }));
393 break;
394
395 case "default-value":
396 formFields.push((0, _preact.h)(FS.DefaultValue, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }));
397 break;
398
399 case "required":
400 formFields.push((0, _preact.h)(FS.Required, { checked: state.required, onChange: (0, _linkstate2.default)(this, 'required') }));
401 break;
402
403 case "wrap":
404 formFields.push((0, _preact.h)(FS.Wrap, { checked: state.wrap, onChange: (0, _linkstate2.default)(this, 'wrap') }));
405 break;
406
407 case "add-to-form":
408 formFields.push((0, _preact.h)(FS.AddToForm, { onSubmit: this.addToForm, onCancel: this.handleCancel }));
409 break;
410
411 case "choices":
412 formFields.push((0, _preact.h)(FS.Choices, { multiple: state.fieldType === 'checkbox', choices: state.choices, handlers: this.choiceHandlers }));
413 break;
414
415 case "button-text":
416 formFields.push((0, _preact.h)(FS.ButtonText, { value: state.value, onChange: (0, _linkstate2.default)(this, 'value') }));
417 break;
418
419 case "accept":
420 formFields.push((0, _preact.h)(FS.Accept, { value: state.accept, onChange: (0, _linkstate2.default)(this, 'accept') }));
421 break;
422
423 }
424 }
425
426 return (0, _preact.h)(
427 'div',
428 { 'class': 'field-config', onKeyPress: FieldConfigurator.handleKeyPress },
429 formFields
430 );
431 }
432 }], [{
433 key: 'handleKeyPress',
434 value: function handleKeyPress(e) {
435 // stop RETURN from submitting the parent form.
436 if (e.keyCode === 13) {
437 e.preventDefault();
438 }
439 }
440 }]);
441
442 return FieldConfigurator;
443 }(_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);
444 exports.FieldConfigurator = FieldConfigurator;
445
446 },{"../field-builder/html.js":7,"./field-settings.js":5,"decko":19,"linkstate":20,"preact":22}],5:[function(require,module,exports){
447 'use strict';
448
449 Object.defineProperty(exports, "__esModule", {
450 value: true
451 });
452 exports.Accept = exports.ButtonText = exports.Choices = exports.Required = exports.Wrap = exports.DefaultValue = exports.Placeholder = exports.Label = exports.AddToForm = undefined;
453
454 var _preact = require('preact');
455
456 function AddToForm(props) {
457 return (0, _preact.h)(
458 'div',
459 { 'class': 'hf-small-margin' },
460 (0, _preact.h)(
461 'button',
462 { 'class': 'button', type: 'button', onClick: props.onSubmit },
463 'Add field to form'
464 ),
465 ' \xA0 ',
466 (0, _preact.h)(
467 'a',
468 { href: 'javascript:void(0);', 'class': 'hf-small', style: 'vertical-align: middle;', onClick: props.onCancel },
469 'or close field helper'
470 )
471 );
472 }
473
474 function Label(props) {
475 return (0, _preact.h)(
476 'div',
477 { 'class': 'hf-small-margin' },
478 (0, _preact.h)(
479 'label',
480 { 'for': 'hf-fg-field-label' },
481 'Field label ',
482 (0, _preact.h)(
483 'span',
484 { 'class': 'hf-required' },
485 '*'
486 )
487 ),
488 (0, _preact.h)('input', { id: 'hf-fg-field-label', type: 'text', value: props.value, onChange: props.onChange })
489 );
490 }
491
492 function Placeholder(props) {
493 return (0, _preact.h)(
494 'div',
495 { 'class': 'hf-small-margin' },
496 (0, _preact.h)(
497 'label',
498 { 'for': 'hf-fg-placeholder' },
499 'Placeholder ',
500 (0, _preact.h)(
501 'span',
502 { 'class': 'hf-italic hf-pull-right' },
503 'Optional'
504 )
505 ),
506 (0, _preact.h)('input', { id: 'hf-fg-placeholder', type: 'text', value: props.value, onChange: props.onChange }),
507 (0, _preact.h)(
508 'p',
509 { 'class': 'help' },
510 'Text to show when field has no value.'
511 )
512 );
513 }
514
515 function ButtonText(props) {
516 return (0, _preact.h)(
517 'div',
518 { 'class': 'hf-small-margin' },
519 (0, _preact.h)(
520 'label',
521 { 'for': 'hf-fg-default-value' },
522 'Button text ',
523 (0, _preact.h)(
524 'span',
525 { 'class': 'hf-required' },
526 '*'
527 )
528 ),
529 (0, _preact.h)('input', { id: 'hf-fg-default-value', type: 'text', value: props.value, onChange: props.onChange }),
530 (0, _preact.h)(
531 'p',
532 { 'class': 'help' },
533 'Text to show on the button.'
534 )
535 );
536 }
537
538 function DefaultValue(props) {
539 return (0, _preact.h)(
540 'div',
541 { 'class': 'hf-small-margin' },
542 (0, _preact.h)(
543 'label',
544 { 'for': 'hf-fg-default-value' },
545 'Default value ',
546 (0, _preact.h)(
547 'span',
548 { 'class': 'hf-italic hf-pull-right' },
549 'Optional'
550 )
551 ),
552 (0, _preact.h)('input', { id: 'hf-fg-default-value', type: 'text', value: props.value, onChange: props.onChange }),
553 (0, _preact.h)(
554 'p',
555 { 'class': 'help' },
556 'Text to pre-fill this field with.'
557 )
558 );
559 }
560
561 function Wrap(props) {
562 return (0, _preact.h)(
563 'div',
564 { 'class': 'hf-small-margin' },
565 (0, _preact.h)(
566 'label',
567 { 'class': 'inline' },
568 (0, _preact.h)('input', { type: 'checkbox', value: '1', defaultChecked: props.checked, onChange: props.onChange }),
569 'Wrap this field in paragraph tags.'
570 )
571 );
572 }
573
574 function Required(props) {
575 return (0, _preact.h)(
576 'div',
577 { 'class': 'hf-small-margin' },
578 (0, _preact.h)(
579 'label',
580 { 'class': 'inline' },
581 (0, _preact.h)('input', { type: 'checkbox', value: '1', defaultChecked: props.checked, onChange: props.onChange }),
582 'This field is required.'
583 )
584 );
585 }
586
587 function Choices(props) {
588 var choiceFields = props.choices.map(function (choice, k) {
589 return (0, _preact.h)(
590 'div',
591 { 'data-key': k },
592 (0, _preact.h)('input', { type: props.multiple ? "checkbox" : "radio", name: 'selected', defaultChecked: choice.checked, onChange: props.handlers.toggleChecked, title: 'Pre-select this choice?' }),
593 (0, _preact.h)('input', { type: 'text', value: choice.label, placeholder: 'Choice label', style: 'width: 80%;', onChange: props.handlers.changeLabel }),
594 (0, _preact.h)(
595 'a',
596 { href: 'javascript:void(0);', onClick: props.handlers.delete, style: 'text-decoration: none;', title: 'Delete choice' },
597 '\u2715'
598 )
599 );
600 });
601
602 return (0, _preact.h)(
603 'div',
604 { 'class': 'hf-small-margin' },
605 (0, _preact.h)(
606 'label',
607 null,
608 'Choices'
609 ),
610 choiceFields,
611 (0, _preact.h)('input', { type: props.multiple ? "checkbox" : "radio", style: 'visibility: hidden;' }),
612 (0, _preact.h)(
613 'a',
614 { href: 'javascript:void(0);', onClick: props.handlers.add },
615 'Add choice'
616 )
617 );
618 }
619
620 function Accept(props) {
621 return (0, _preact.h)(
622 'div',
623 { 'class': 'hf-small-margin' },
624 (0, _preact.h)(
625 'label',
626 null,
627 'Accepted file types'
628 ),
629 (0, _preact.h)('input', { type: 'text', value: props.value, onChange: props.onChange }),
630 (0, _preact.h)(
631 'p',
632 { 'class': 'help' },
633 'Use a comma-separated list of accepted file extensions, eg ',
634 (0, _preact.h)(
635 'code',
636 null,
637 '.pdf'
638 ),
639 '. ',
640 (0, _preact.h)('br', null),
641 'Leave empty to accept any file type.'
642 )
643 );
644 }
645
646 exports.AddToForm = AddToForm;
647 exports.Label = Label;
648 exports.Placeholder = Placeholder;
649 exports.DefaultValue = DefaultValue;
650 exports.Wrap = Wrap;
651 exports.Required = Required;
652 exports.Choices = Choices;
653 exports.ButtonText = ButtonText;
654 exports.Accept = Accept;
655
656 },{"preact":22}],6:[function(require,module,exports){
657 'use strict';
658
659 // imports
660
661 Object.defineProperty(exports, "__esModule", {
662 value: true
663 });
664
665 var _preact = require('preact');
666
667 var _fieldBuilder = require('./components/field-builder.js');
668
669 // vars
670 var rootElement = void 0;
671 var Editor = void 0;
672 var fields = void 0;
673
674 // functions
675 function Field(key, label, configRows) {
676 this.key = key;
677 this.label = label;
678 this.configRows = configRows || [];
679 }
680
681 function mount() {
682 rootElement = (0, _preact.render)((0, _preact.h)(_fieldBuilder.FieldBuilder, { fields: fields }), document.getElementById('hf-field-builder'), rootElement);
683 }
684
685 // bootstrap
686 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("checkbox", "Checkboxes", ["label", "choices", "wrap", "add-to-form"]), new Field("radio", "Radio buttons", ["label", "choices", "wrap", "add-to-form"]), new Field("submit", "Submit button", ["button-text", "wrap", "add-to-form"])];
687
688 exports.default = {
689 init: function init() {
690 mount();
691 },
692
693 registerField: function registerField(key, label, configRows) {
694 fields.push(new Field(key, label, configRows));
695 mount();
696 }
697 };
698
699 },{"./components/field-builder.js":3,"preact":22}],7:[function(require,module,exports){
700 'use strict';
701
702 Object.defineProperty(exports, "__esModule", {
703 value: true
704 });
705 exports.htmlgenerate = undefined;
706
707 var _preactRenderToString = require('preact-render-to-string');
708
709 var _preactRenderToString2 = _interopRequireDefault(_preactRenderToString);
710
711 var _preact = require('preact');
712
713 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
714
715 function htmlgenerate(conf) {
716 var fieldName = namify(conf.fieldLabel);
717 var fieldId = conf.formSlug + '-' + fieldName;
718 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)("label", {
719 "for": fieldId
720 }, conf.fieldLabel) : "";
721 var fieldAttr = void 0,
722 field = void 0;
723
724 switch (conf.fieldType) {
725 case "text":
726 default:
727 fieldAttr = {
728 type: conf.fieldType,
729 name: fieldName,
730 value: conf.value,
731 placeholder: conf.placeholder,
732 required: conf.required,
733 id: fieldId
734 };
735 field = html("input", fieldAttr);
736 break;
737 case "textarea":
738 fieldAttr = {
739 name: fieldName,
740 placeholder: conf.placeholder,
741 required: conf.required,
742 id: fieldId
743 };
744 field = html("textarea", fieldAttr, conf.value);
745 break;
746
747 case "dropdown":
748 fieldAttr = {
749 name: fieldName,
750 required: conf.required,
751 id: fieldId
752 };
753 var opts = conf.choices.map(function (choice) {
754 return html("option", { selected: choice.checked }, choice.label);
755 });
756 field = html("select", fieldAttr, opts);
757 break;
758
759 case "radio":
760 field = conf.choices.map(function (choice) {
761 return html("label", {}, [html("input", {
762 type: "radio",
763 name: fieldName,
764 value: choice.label,
765 selected: choice.checked
766 }), " ", html("span", {}, choice.label)]);
767 });
768 break;
769
770 case "checkbox":
771 field = conf.choices.map(function (choice) {
772 return html("label", {}, [html("input", {
773 type: "checkbox",
774 name: fieldName + "[]",
775 value: choice.label,
776 checked: choice.checked
777 }), " ", html("span", {}, choice.label)]);
778 });
779 break;
780
781 case "file":
782 fieldAttr = {
783 type: "file",
784 name: fieldName,
785 required: conf.required,
786 id: fieldId
787 };
788
789 if (conf['accept']) {
790 fieldAttr['accept'] = conf['accept'];
791 }
792
793 field = html("input", fieldAttr);
794 break;
795
796 case "submit":
797 fieldAttr = {
798 type: "submit",
799 value: conf.value
800 };
801 field = html("input", fieldAttr);
802 break;
803
804 }
805
806 var str = "";
807 if (conf.wrap) {
808 var tmpl = (0, _preact.h)("p", {}, [label, field]);
809 str = (0, _preactRenderToString2.default)(tmpl, null, { pretty: true });
810 } else {
811 str += (0, _preactRenderToString2.default)(label, null, { pretty: true });
812 str += "\n";
813 str += (0, _preactRenderToString2.default)(field, null, { pretty: true });
814 }
815
816 return str;
817 }
818
819 function html(tag, attr, children) {
820 return (0, _preact.h)(tag, filterEmptyObjectValues(attr), children);
821 }
822
823 function namify(str) {
824 return str.replace(/ /g, '_').replace(/[^\w\[\]_]*/g, "").toUpperCase();
825 }
826
827 function filterEmptyObjectValues(obj) {
828 var newObj = {};
829 for (var propName in obj) {
830 if (obj[propName] !== false && obj[propName] !== "") {
831 newObj[propName] = obj[propName];
832 }
833 }
834 return newObj;
835 }
836
837 exports.htmlgenerate = htmlgenerate;
838
839 },{"preact":22,"preact-render-to-string":21}],8:[function(require,module,exports){
840 'use strict';
841
842 Object.defineProperty(exports, "__esModule", {
843 value: true
844 });
845 var availableActions = void 0,
846 actionTemplates = void 0,
847 actions = void 0;
848
849 function init() {
850 actions = document.getElementById('hf-form-actions');
851 availableActions = document.getElementById('hf-available-form-actions');
852 actionTemplates = document.getElementById('hf-form-action-templates');
853
854 // turn settings into accordions
855 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
856 el.parentNode.removeChild(el);
857
858 var heading = el.getAttribute('data-title');
859 var summary = el.querySelector('.hf-action-summary');
860 if (summary) {
861 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
862 }
863 var wrap = createAccordion(heading, el.innerHTML);
864 actions.appendChild(wrap);
865
866 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
867 });
868
869 availableActions.addEventListener('click', addAction, true);
870 }
871
872 function createAccordion(headingHTML, contentHTML) {
873 var wrap = document.createElement('div');
874 wrap.className = "hf-accordion expanded ";
875
876 var heading = document.createElement('h4');
877 heading.className = "hf-accordion-heading";
878 heading.innerHTML = headingHTML;
879 wrap.appendChild(heading);
880
881 var content = document.createElement('div');
882 content.className = "hf-accordion-content";
883 content.innerHTML = contentHTML;
884 wrap.appendChild(content);
885
886 var deleteWrap = document.createElement('p');
887 deleteWrap.style.textAlign = 'right';
888 var deleteLink = document.createElement('a');
889 deleteLink.href = 'javascript:void(0);';
890 deleteLink.className = "danger";
891 deleteLink.innerText = 'Delete this action';
892 deleteWrap.appendChild(deleteLink);
893 content.appendChild(deleteWrap);
894
895 // bind handlers
896 heading.addEventListener('click', createToggleActionHandler(wrap, content));
897 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
898 return wrap;
899 }
900
901 function addAction(e) {
902 var el = e.target || e.srcElement;
903 if (el.tagName !== 'INPUT') {
904 return;
905 }
906
907 var actionType = el.getAttribute('data-action-type');
908 var actionTemplate = actionTemplates.querySelector('#hf-action-type-' + actionType + '-template');
909
910 // append HTML to actions wrapper
911 var index = actions.querySelectorAll('div').length - 1;
912 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
913 actions.appendChild(wrap);
914
915 // hide "no form actions" message
916 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
917 }
918
919 function createDeleteActionHandler(wrap) {
920 return function () {
921 actions.removeChild(wrap);
922
923 if (actions.childElementCount === 1) {
924 actions.querySelector('#hf-form-actions-empty').style.display = '';
925 }
926 };
927 }
928
929 function createToggleActionHandler(wrap, content) {
930 return function () {
931 var show = content.offsetParent === null;
932 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
933 content.style.display = show ? 'block' : 'none';
934 };
935 }
936
937 exports.default = { init: init };
938
939 },{}],9:[function(require,module,exports){
940 'use strict';
941
942 // load CodeMirror & plugins
943
944 Object.defineProperty(exports, "__esModule", {
945 value: true
946 });
947 var CodeMirror = require('codemirror');
948 require('codemirror/mode/xml/xml');
949 require('codemirror/mode/javascript/javascript');
950 require('codemirror/mode/css/css');
951 require('codemirror/mode/htmlmixed/htmlmixed');
952 require('codemirror/addon/fold/xml-fold');
953 require('codemirror/addon/edit/matchtags');
954 require('codemirror/addon/edit/closetag.js');
955
956 var editor = void 0,
957 element = void 0,
958 dom = void 0,
959 requiredFieldsInput = void 0,
960 emailFieldsInput = void 0,
961 previewFrame = void 0,
962 previewDom = void 0;
963 var templateRegex = /\{\{ *(\w+)(?:\.([\w\.]+))? *(?:\|\| *(\w+))? *\}\}/g;
964
965 function init() {
966 previewFrame = document.getElementById('hf-form-preview');
967 element = document.getElementById('hf-form-editor');
968 dom = document.createElement('form');
969 requiredFieldsInput = document.getElementById('hf-required-fields');
970 emailFieldsInput = document.getElementById('hf-email-fields');
971
972 dom.innerHTML = element.value;
973 editor = CodeMirror.fromTextArea(element, {
974 selectionPointer: true,
975 matchTags: { bothTags: true },
976 mode: "htmlmixed",
977 htmlMode: true,
978 autoCloseTags: true,
979 autoRefresh: true,
980 styleActiveLine: true,
981 matchBrackets: true
982 });
983
984 editor.on('changes', debounce(updatePreview, 500));
985 editor.on('changes', debounce(updateShadowDOM, 100));
986 editor.on('changes', debounce(updateFieldVariables, 500));
987 editor.on('blur', updatePreview);
988 editor.on('blur', updateShadowDOM);
989 editor.on('blur', updateFieldVariables);
990 editor.on('blur', updateRequiredFields);
991 editor.on('blur', updateEmailFields);
992
993 previewFrame.addEventListener('load', setPreviewDom);
994 setPreviewDom();
995 updateFieldVariables();
996 }
997
998 function setPreviewDom() {
999 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
1000 previewDom = frameContent.querySelector('.hf-fields-wrap');
1001
1002 if (previewDom) {
1003 updatePreview();
1004 }
1005 }
1006
1007 function getFieldVariableName(f) {
1008 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
1009 }
1010
1011 function updateFieldVariables() {
1012 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
1013 var fieldVariables = uniq([].map.call(fields, function (f) {
1014 return '[' + getFieldVariableName(f) + ']';
1015 }));
1016 var wpbody = document.getElementById('wpbody-content');
1017
1018 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
1019 // remove existing variables
1020 while (el.firstChild) {
1021 el.removeChild(el.firstChild);
1022 }
1023
1024 var variableElements = fieldVariables.map(function (n) {
1025 // measure width of actual font size for prettiness
1026 var sizeEl = document.createElement('span');
1027 sizeEl.style.visibility = 'hidden';
1028 sizeEl.innerText = n;
1029 wpbody.appendChild(sizeEl);
1030 var width = sizeEl.offsetWidth;
1031 wpbody.removeChild(sizeEl);
1032
1033 // add input el
1034 var el = document.createElement('input');
1035 el.setAttribute('type', 'text');
1036 el.style.maxWidth = width * 1.1 + 14 + 'px';
1037 el.setAttribute('value', n);
1038 el.setAttribute('readonly', true);
1039 el.setAttribute('onfocus', 'this.select()');
1040 return el;
1041 });
1042
1043 variableElements.forEach(function (vel, i, arr) {
1044 el.appendChild(vel);
1045 });
1046 });
1047 }
1048
1049 function updatePreview() {
1050 var markup = editor.getValue();
1051
1052 // replace template tags
1053 markup = markup.replace(templateRegex, function (s, m) {
1054 if (arguments[3]) {
1055 return arguments[3];
1056 }
1057
1058 return '';
1059 });
1060
1061 // update dom
1062 previewDom.innerHTML = markup;
1063 previewDom.dispatchEvent(new Event('hf-refresh'));
1064 }
1065
1066 function updateShadowDOM() {
1067 dom.innerHTML = editor.getValue();
1068 }
1069
1070 function updateRequiredFields() {
1071 var fields = dom.querySelectorAll('[required]');
1072 var fieldNames = [].map.call(fields, getFieldVariableName);
1073 requiredFieldsInput.value = fieldNames.join(',');
1074 }
1075
1076 function updateEmailFields() {
1077 var fields = dom.querySelectorAll('input[type="email"]');
1078 var fieldNames = [].map.call(fields, getFieldVariableName);
1079 emailFieldsInput.value = fieldNames.join(',');
1080 }
1081
1082 function replaceSelection(str) {
1083 editor.replaceSelection(str);
1084 editor.focus();
1085 }
1086
1087 function debounce(func, wait, immediate) {
1088 var timeout;
1089 return function () {
1090 var context = this,
1091 args = arguments;
1092 var later = function later() {
1093 timeout = null;
1094 if (!immediate) func.apply(context, args);
1095 };
1096 var callNow = immediate && !timeout;
1097 clearTimeout(timeout);
1098 timeout = setTimeout(later, wait);
1099 if (callNow) func.apply(context, args);
1100 };
1101 };
1102
1103 function uniq(a) {
1104 var seen = {};
1105 return a.filter(function (item) {
1106 return seen.hasOwnProperty(item) ? false : seen[item] = true;
1107 });
1108 }
1109
1110 exports.default = {
1111 init: init,
1112 replaceSelection: replaceSelection
1113 };
1114
1115 },{"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){
1116 'use strict';
1117
1118 Object.defineProperty(exports, "__esModule", {
1119 value: true
1120 });
1121 var tabs = void 0,
1122 tabNavs = void 0;
1123
1124 function init() {
1125 tabs = document.querySelectorAll('.hf-tab');
1126 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1127 for (var i = 0; i < tabNavs.length; i++) {
1128 tabNavs[i].addEventListener('click', open);
1129 }
1130 };
1131
1132 function open(e) {
1133 var tabTarget = this.getAttribute('data-tab-target');
1134 for (var i = 0; i < tabNavs.length; i++) {
1135 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1136 }
1137 this.blur();
1138
1139 for (var _i = 0; _i < tabs.length; _i++) {
1140 var tab = tabs[_i];
1141 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1142 }
1143
1144 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + " ");
1145
1146 if (window.history) {
1147 var newUrl = window.location.href;
1148 newUrl = newUrl.replace(/\&tab\=\w+/, "");
1149 newUrl += "&tab=" + tabTarget;
1150
1151 window.history.replaceState({ tab: tabTarget }, document.title, newUrl);
1152 }
1153
1154 e.preventDefault();
1155 };
1156
1157 exports.default = {
1158 init: init,
1159 open: open
1160 };
1161
1162 },{}],11:[function(require,module,exports){
1163 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1164 // Distributed under an MIT license: http://codemirror.net/LICENSE
1165
1166 /**
1167 * Tag-closer extension for CodeMirror.
1168 *
1169 * This extension adds an "autoCloseTags" option that can be set to
1170 * either true to get the default behavior, or an object to further
1171 * configure its behavior.
1172 *
1173 * These are supported options:
1174 *
1175 * `whenClosing` (default true)
1176 * Whether to autoclose when the '/' of a closing tag is typed.
1177 * `whenOpening` (default true)
1178 * Whether to autoclose the tag when the final '>' of an opening
1179 * tag is typed.
1180 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1181 * An array of tag names that should not be autoclosed.
1182 * `indentTags` (default is block tags for HTML, none for XML)
1183 * An array of tag names that should, when opened, cause a
1184 * blank line to be added inside the tag, and the blank line and
1185 * closing line to be indented.
1186 *
1187 * See demos/closetag.html for a usage example.
1188 */
1189
1190 (function(mod) {
1191 if (typeof exports == "object" && typeof module == "object") // CommonJS
1192 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1193 else if (typeof define == "function" && define.amd) // AMD
1194 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1195 else // Plain browser env
1196 mod(CodeMirror);
1197 })(function(CodeMirror) {
1198 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1199 if (old != CodeMirror.Init && old)
1200 cm.removeKeyMap("autoCloseTags");
1201 if (!val) return;
1202 var map = {name: "autoCloseTags"};
1203 if (typeof val != "object" || val.whenClosing)
1204 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1205 if (typeof val != "object" || val.whenOpening)
1206 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1207 cm.addKeyMap(map);
1208 });
1209
1210 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1211 "source", "track", "wbr"];
1212 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1213 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1214
1215 function autoCloseGT(cm) {
1216 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1217 var ranges = cm.listSelections(), replacements = [];
1218 var opt = cm.getOption("autoCloseTags");
1219 for (var i = 0; i < ranges.length; i++) {
1220 if (!ranges[i].empty()) return CodeMirror.Pass;
1221 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1222 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1223 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1224
1225 var html = inner.mode.configuration == "html";
1226 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1227 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1228
1229 var tagName = state.tagName;
1230 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1231 var lowerTagName = tagName.toLowerCase();
1232 // Don't process the '>' at the end of an end-tag or self-closing tag
1233 if (!tagName ||
1234 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1235 tok.type == "tag" && state.type == "closeTag" ||
1236 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1237 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1238 closingTagExists(cm, tagName, pos, state, true))
1239 return CodeMirror.Pass;
1240
1241 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1242 replacements[i] = {indent: indent,
1243 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1244 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1245 }
1246
1247 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1248 for (var i = ranges.length - 1; i >= 0; i--) {
1249 var info = replacements[i];
1250 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1251 var sel = cm.listSelections().slice(0);
1252 sel[i] = {head: info.newPos, anchor: info.newPos};
1253 cm.setSelections(sel);
1254 if (!dontIndentOnAutoClose && info.indent) {
1255 cm.indentLine(info.newPos.line, null, true);
1256 cm.indentLine(info.newPos.line + 1, null, true);
1257 }
1258 }
1259 }
1260
1261 function autoCloseCurrent(cm, typingSlash) {
1262 var ranges = cm.listSelections(), replacements = [];
1263 var head = typingSlash ? "/" : "</";
1264 var opt = cm.getOption("autoCloseTags");
1265 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1266 for (var i = 0; i < ranges.length; i++) {
1267 if (!ranges[i].empty()) return CodeMirror.Pass;
1268 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1269 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1270 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1271 tok.start != pos.ch - 1))
1272 return CodeMirror.Pass;
1273 // Kludge to get around the fact that we are not in XML mode
1274 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1275 // work for other XML embedded languages (there is no general
1276 // way to go from a mixed mode to its current XML state).
1277 var replacement;
1278 if (inner.mode.name != "xml") {
1279 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1280 replacement = head + "script";
1281 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1282 replacement = head + "style";
1283 else
1284 return CodeMirror.Pass;
1285 } else {
1286 if (!state.context || !state.context.tagName ||
1287 closingTagExists(cm, state.context.tagName, pos, state))
1288 return CodeMirror.Pass;
1289 replacement = head + state.context.tagName;
1290 }
1291 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1292 replacements[i] = replacement;
1293 }
1294 cm.replaceSelections(replacements);
1295 ranges = cm.listSelections();
1296 if (!dontIndentOnAutoClose) {
1297 for (var i = 0; i < ranges.length; i++)
1298 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1299 cm.indentLine(ranges[i].head.line);
1300 }
1301 }
1302
1303 function autoCloseSlash(cm) {
1304 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1305 return autoCloseCurrent(cm, true);
1306 }
1307
1308 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1309
1310 function indexOf(collection, elt) {
1311 if (collection.indexOf) return collection.indexOf(elt);
1312 for (var i = 0, e = collection.length; i < e; ++i)
1313 if (collection[i] == elt) return i;
1314 return -1;
1315 }
1316
1317 // If xml-fold is loaded, we use its functionality to try and verify
1318 // whether a given tag is actually unclosed.
1319 function closingTagExists(cm, tagName, pos, state, newTag) {
1320 if (!CodeMirror.scanForClosingTag) return false;
1321 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1322 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1323 if (!nextClose || nextClose.tag != tagName) return false;
1324 var cx = state.context;
1325 // If the immediate wrapping context contains onCx instances of
1326 // the same tag, a closing tag only exists if there are at least
1327 // that many closing tags of that type following.
1328 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1329 pos = nextClose.to;
1330 for (var i = 1; i < onCx; i++) {
1331 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1332 if (!next || next.tag != tagName) return false;
1333 pos = next.to;
1334 }
1335 return true;
1336 }
1337 });
1338
1339 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],12:[function(require,module,exports){
1340 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1341 // Distributed under an MIT license: http://codemirror.net/LICENSE
1342
1343 (function(mod) {
1344 if (typeof exports == "object" && typeof module == "object") // CommonJS
1345 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1346 else if (typeof define == "function" && define.amd) // AMD
1347 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1348 else // Plain browser env
1349 mod(CodeMirror);
1350 })(function(CodeMirror) {
1351 "use strict";
1352
1353 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1354 if (old && old != CodeMirror.Init) {
1355 cm.off("cursorActivity", doMatchTags);
1356 cm.off("viewportChange", maybeUpdateMatch);
1357 clear(cm);
1358 }
1359 if (val) {
1360 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1361 cm.on("cursorActivity", doMatchTags);
1362 cm.on("viewportChange", maybeUpdateMatch);
1363 doMatchTags(cm);
1364 }
1365 });
1366
1367 function clear(cm) {
1368 if (cm.state.tagHit) cm.state.tagHit.clear();
1369 if (cm.state.tagOther) cm.state.tagOther.clear();
1370 cm.state.tagHit = cm.state.tagOther = null;
1371 }
1372
1373 function doMatchTags(cm) {
1374 cm.state.failedTagMatch = false;
1375 cm.operation(function() {
1376 clear(cm);
1377 if (cm.somethingSelected()) return;
1378 var cur = cm.getCursor(), range = cm.getViewport();
1379 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1380 var match = CodeMirror.findMatchingTag(cm, cur, range);
1381 if (!match) return;
1382 if (cm.state.matchBothTags) {
1383 var hit = match.at == "open" ? match.open : match.close;
1384 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1385 }
1386 var other = match.at == "close" ? match.open : match.close;
1387 if (other)
1388 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1389 else
1390 cm.state.failedTagMatch = true;
1391 });
1392 }
1393
1394 function maybeUpdateMatch(cm) {
1395 if (cm.state.failedTagMatch) doMatchTags(cm);
1396 }
1397
1398 CodeMirror.commands.toMatchingTag = function(cm) {
1399 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1400 if (found) {
1401 var other = found.at == "close" ? found.open : found.close;
1402 if (other) cm.extendSelection(other.to, other.from);
1403 }
1404 };
1405 });
1406
1407 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],13:[function(require,module,exports){
1408 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1409 // Distributed under an MIT license: http://codemirror.net/LICENSE
1410
1411 (function(mod) {
1412 if (typeof exports == "object" && typeof module == "object") // CommonJS
1413 mod(require("../../lib/codemirror"));
1414 else if (typeof define == "function" && define.amd) // AMD
1415 define(["../../lib/codemirror"], mod);
1416 else // Plain browser env
1417 mod(CodeMirror);
1418 })(function(CodeMirror) {
1419 "use strict";
1420
1421 var Pos = CodeMirror.Pos;
1422 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1423
1424 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";
1425 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1426 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1427
1428 function Iter(cm, line, ch, range) {
1429 this.line = line; this.ch = ch;
1430 this.cm = cm; this.text = cm.getLine(line);
1431 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1432 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1433 }
1434
1435 function tagAt(iter, ch) {
1436 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1437 return type && /\btag\b/.test(type);
1438 }
1439
1440 function nextLine(iter) {
1441 if (iter.line >= iter.max) return;
1442 iter.ch = 0;
1443 iter.text = iter.cm.getLine(++iter.line);
1444 return true;
1445 }
1446 function prevLine(iter) {
1447 if (iter.line <= iter.min) return;
1448 iter.text = iter.cm.getLine(--iter.line);
1449 iter.ch = iter.text.length;
1450 return true;
1451 }
1452
1453 function toTagEnd(iter) {
1454 for (;;) {
1455 var gt = iter.text.indexOf(">", iter.ch);
1456 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1457 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1458 var lastSlash = iter.text.lastIndexOf("/", gt);
1459 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1460 iter.ch = gt + 1;
1461 return selfClose ? "selfClose" : "regular";
1462 }
1463 }
1464 function toTagStart(iter) {
1465 for (;;) {
1466 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1467 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1468 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1469 xmlTagStart.lastIndex = lt;
1470 iter.ch = lt;
1471 var match = xmlTagStart.exec(iter.text);
1472 if (match && match.index == lt) return match;
1473 }
1474 }
1475
1476 function toNextTag(iter) {
1477 for (;;) {
1478 xmlTagStart.lastIndex = iter.ch;
1479 var found = xmlTagStart.exec(iter.text);
1480 if (!found) { if (nextLine(iter)) continue; else return; }
1481 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1482 iter.ch = found.index + found[0].length;
1483 return found;
1484 }
1485 }
1486 function toPrevTag(iter) {
1487 for (;;) {
1488 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1489 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1490 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1491 var lastSlash = iter.text.lastIndexOf("/", gt);
1492 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1493 iter.ch = gt + 1;
1494 return selfClose ? "selfClose" : "regular";
1495 }
1496 }
1497
1498 function findMatchingClose(iter, tag) {
1499 var stack = [];
1500 for (;;) {
1501 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1502 if (!next || !(end = toTagEnd(iter))) return;
1503 if (end == "selfClose") continue;
1504 if (next[1]) { // closing tag
1505 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1506 stack.length = i;
1507 break;
1508 }
1509 if (i < 0 && (!tag || tag == next[2])) return {
1510 tag: next[2],
1511 from: Pos(startLine, startCh),
1512 to: Pos(iter.line, iter.ch)
1513 };
1514 } else { // opening tag
1515 stack.push(next[2]);
1516 }
1517 }
1518 }
1519 function findMatchingOpen(iter, tag) {
1520 var stack = [];
1521 for (;;) {
1522 var prev = toPrevTag(iter);
1523 if (!prev) return;
1524 if (prev == "selfClose") { toTagStart(iter); continue; }
1525 var endLine = iter.line, endCh = iter.ch;
1526 var start = toTagStart(iter);
1527 if (!start) return;
1528 if (start[1]) { // closing tag
1529 stack.push(start[2]);
1530 } else { // opening tag
1531 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1532 stack.length = i;
1533 break;
1534 }
1535 if (i < 0 && (!tag || tag == start[2])) return {
1536 tag: start[2],
1537 from: Pos(iter.line, iter.ch),
1538 to: Pos(endLine, endCh)
1539 };
1540 }
1541 }
1542 }
1543
1544 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1545 var iter = new Iter(cm, start.line, 0);
1546 for (;;) {
1547 var openTag = toNextTag(iter), end;
1548 if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
1549 if (!openTag[1] && end != "selfClose") {
1550 var startPos = Pos(iter.line, iter.ch);
1551 var endPos = findMatchingClose(iter, openTag[2]);
1552 return endPos && {from: startPos, to: endPos.from};
1553 }
1554 }
1555 });
1556 CodeMirror.findMatchingTag = function(cm, pos, range) {
1557 var iter = new Iter(cm, pos.line, pos.ch, range);
1558 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1559 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1560 var start = end && toTagStart(iter);
1561 if (!end || !start || cmp(iter, pos) > 0) return;
1562 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1563 if (end == "selfClose") return {open: here, close: null, at: "open"};
1564
1565 if (start[1]) { // closing tag
1566 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1567 } else { // opening tag
1568 iter = new Iter(cm, to.line, to.ch, range);
1569 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1570 }
1571 };
1572
1573 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1574 var iter = new Iter(cm, pos.line, pos.ch, range);
1575 for (;;) {
1576 var open = findMatchingOpen(iter, tag);
1577 if (!open) break;
1578 var forward = new Iter(cm, pos.line, pos.ch, range);
1579 var close = findMatchingClose(forward, open.tag);
1580 if (close) return {open: open, close: close};
1581 }
1582 };
1583
1584 // Used by addon/edit/closetag.js
1585 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1586 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1587 return findMatchingClose(iter, name);
1588 };
1589 });
1590
1591 },{"../../lib/codemirror":14}],14:[function(require,module,exports){
1592 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1593 // Distributed under an MIT license: http://codemirror.net/LICENSE
1594
1595 // This is CodeMirror (http://codemirror.net), a code editor
1596 // implemented in JavaScript on top of the browser's DOM.
1597 //
1598 // You can find some technical background for some of the code below
1599 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1600
1601 (function (global, factory) {
1602 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1603 typeof define === 'function' && define.amd ? define(factory) :
1604 (global.CodeMirror = factory());
1605 }(this, (function () { 'use strict';
1606
1607 // Kludges for bugs and behavior differences that can't be feature
1608 // detected are enabled based on userAgent etc sniffing.
1609 var userAgent = navigator.userAgent;
1610 var platform = navigator.platform;
1611
1612 var gecko = /gecko\/\d/i.test(userAgent);
1613 var ie_upto10 = /MSIE \d/.test(userAgent);
1614 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1615 var edge = /Edge\/(\d+)/.exec(userAgent);
1616 var ie = ie_upto10 || ie_11up || edge;
1617 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1618 var webkit = !edge && /WebKit\//.test(userAgent);
1619 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1620 var chrome = !edge && /Chrome\//.test(userAgent);
1621 var presto = /Opera\//.test(userAgent);
1622 var safari = /Apple Computer/.test(navigator.vendor);
1623 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1624 var phantom = /PhantomJS/.test(userAgent);
1625
1626 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1627 var android = /Android/.test(userAgent);
1628 // This is woefully incomplete. Suggestions for alternative methods welcome.
1629 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1630 var mac = ios || /Mac/.test(platform);
1631 var chromeOS = /\bCrOS\b/.test(userAgent);
1632 var windows = /win/i.test(platform);
1633
1634 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1635 if (presto_version) { presto_version = Number(presto_version[1]); }
1636 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1637 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1638 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1639 var captureRightClick = gecko || (ie && ie_version >= 9);
1640
1641 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1642
1643 var rmClass = function(node, cls) {
1644 var current = node.className;
1645 var match = classTest(cls).exec(current);
1646 if (match) {
1647 var after = current.slice(match.index + match[0].length);
1648 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1649 }
1650 };
1651
1652 function removeChildren(e) {
1653 for (var count = e.childNodes.length; count > 0; --count)
1654 { e.removeChild(e.firstChild); }
1655 return e
1656 }
1657
1658 function removeChildrenAndAdd(parent, e) {
1659 return removeChildren(parent).appendChild(e)
1660 }
1661
1662 function elt(tag, content, className, style) {
1663 var e = document.createElement(tag);
1664 if (className) { e.className = className; }
1665 if (style) { e.style.cssText = style; }
1666 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1667 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1668 return e
1669 }
1670 // wrapper for elt, which removes the elt from the accessibility tree
1671 function eltP(tag, content, className, style) {
1672 var e = elt(tag, content, className, style);
1673 e.setAttribute("role", "presentation");
1674 return e
1675 }
1676
1677 var range;
1678 if (document.createRange) { range = function(node, start, end, endNode) {
1679 var r = document.createRange();
1680 r.setEnd(endNode || node, end);
1681 r.setStart(node, start);
1682 return r
1683 }; }
1684 else { range = function(node, start, end) {
1685 var r = document.body.createTextRange();
1686 try { r.moveToElementText(node.parentNode); }
1687 catch(e) { return r }
1688 r.collapse(true);
1689 r.moveEnd("character", end);
1690 r.moveStart("character", start);
1691 return r
1692 }; }
1693
1694 function contains(parent, child) {
1695 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1696 { child = child.parentNode; }
1697 if (parent.contains)
1698 { return parent.contains(child) }
1699 do {
1700 if (child.nodeType == 11) { child = child.host; }
1701 if (child == parent) { return true }
1702 } while (child = child.parentNode)
1703 }
1704
1705 function activeElt() {
1706 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1707 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1708 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1709 var activeElement;
1710 try {
1711 activeElement = document.activeElement;
1712 } catch(e) {
1713 activeElement = document.body || null;
1714 }
1715 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1716 { activeElement = activeElement.shadowRoot.activeElement; }
1717 return activeElement
1718 }
1719
1720 function addClass(node, cls) {
1721 var current = node.className;
1722 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1723 }
1724 function joinClasses(a, b) {
1725 var as = a.split(" ");
1726 for (var i = 0; i < as.length; i++)
1727 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1728 return b
1729 }
1730
1731 var selectInput = function(node) { node.select(); };
1732 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1733 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1734 else if (ie) // Suppress mysterious IE10 errors
1735 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1736
1737 function bind(f) {
1738 var args = Array.prototype.slice.call(arguments, 1);
1739 return function(){return f.apply(null, args)}
1740 }
1741
1742 function copyObj(obj, target, overwrite) {
1743 if (!target) { target = {}; }
1744 for (var prop in obj)
1745 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1746 { target[prop] = obj[prop]; } }
1747 return target
1748 }
1749
1750 // Counts the column offset in a string, taking tabs into account.
1751 // Used mostly to find indentation.
1752 function countColumn(string, end, tabSize, startIndex, startValue) {
1753 if (end == null) {
1754 end = string.search(/[^\s\u00a0]/);
1755 if (end == -1) { end = string.length; }
1756 }
1757 for (var i = startIndex || 0, n = startValue || 0;;) {
1758 var nextTab = string.indexOf("\t", i);
1759 if (nextTab < 0 || nextTab >= end)
1760 { return n + (end - i) }
1761 n += nextTab - i;
1762 n += tabSize - (n % tabSize);
1763 i = nextTab + 1;
1764 }
1765 }
1766
1767 var Delayed = function() {this.id = null;};
1768 Delayed.prototype.set = function (ms, f) {
1769 clearTimeout(this.id);
1770 this.id = setTimeout(f, ms);
1771 };
1772
1773 function indexOf(array, elt) {
1774 for (var i = 0; i < array.length; ++i)
1775 { if (array[i] == elt) { return i } }
1776 return -1
1777 }
1778
1779 // Number of pixels added to scroller and sizer to hide scrollbar
1780 var scrollerGap = 30;
1781
1782 // Returned or thrown by various protocols to signal 'I'm not
1783 // handling this'.
1784 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1785
1786 // Reused option objects for setSelection & friends
1787 var sel_dontScroll = {scroll: false};
1788 var sel_mouse = {origin: "*mouse"};
1789 var sel_move = {origin: "+move"};
1790
1791 // The inverse of countColumn -- find the offset that corresponds to
1792 // a particular column.
1793 function findColumn(string, goal, tabSize) {
1794 for (var pos = 0, col = 0;;) {
1795 var nextTab = string.indexOf("\t", pos);
1796 if (nextTab == -1) { nextTab = string.length; }
1797 var skipped = nextTab - pos;
1798 if (nextTab == string.length || col + skipped >= goal)
1799 { return pos + Math.min(skipped, goal - col) }
1800 col += nextTab - pos;
1801 col += tabSize - (col % tabSize);
1802 pos = nextTab + 1;
1803 if (col >= goal) { return pos }
1804 }
1805 }
1806
1807 var spaceStrs = [""];
1808 function spaceStr(n) {
1809 while (spaceStrs.length <= n)
1810 { spaceStrs.push(lst(spaceStrs) + " "); }
1811 return spaceStrs[n]
1812 }
1813
1814 function lst(arr) { return arr[arr.length-1] }
1815
1816 function map(array, f) {
1817 var out = [];
1818 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1819 return out
1820 }
1821
1822 function insertSorted(array, value, score) {
1823 var pos = 0, priority = score(value);
1824 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1825 array.splice(pos, 0, value);
1826 }
1827
1828 function nothing() {}
1829
1830 function createObj(base, props) {
1831 var inst;
1832 if (Object.create) {
1833 inst = Object.create(base);
1834 } else {
1835 nothing.prototype = base;
1836 inst = new nothing();
1837 }
1838 if (props) { copyObj(props, inst); }
1839 return inst
1840 }
1841
1842 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1843 function isWordCharBasic(ch) {
1844 return /\w/.test(ch) || ch > "\x80" &&
1845 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1846 }
1847 function isWordChar(ch, helper) {
1848 if (!helper) { return isWordCharBasic(ch) }
1849 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1850 return helper.test(ch)
1851 }
1852
1853 function isEmpty(obj) {
1854 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1855 return true
1856 }
1857
1858 // Extending unicode characters. A series of a non-extending char +
1859 // any number of extending chars is treated as a single unit as far
1860 // as editing and measuring is concerned. This is not fully correct,
1861 // since some scripts/fonts/browsers also treat other configurations
1862 // of code points as a group.
1863 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]/;
1864 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1865
1866 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1867 function skipExtendingChars(str, pos, dir) {
1868 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1869 return pos
1870 }
1871
1872 // Returns the value from the range [`from`; `to`] that satisfies
1873 // `pred` and is closest to `from`. Assumes that at least `to`
1874 // satisfies `pred`. Supports `from` being greater than `to`.
1875 function findFirst(pred, from, to) {
1876 // At any point we are certain `to` satisfies `pred`, don't know
1877 // whether `from` does.
1878 var dir = from > to ? -1 : 1;
1879 for (;;) {
1880 if (from == to) { return from }
1881 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1882 if (mid == from) { return pred(mid) ? from : to }
1883 if (pred(mid)) { to = mid; }
1884 else { from = mid + dir; }
1885 }
1886 }
1887
1888 // The display handles the DOM integration, both for input reading
1889 // and content drawing. It holds references to DOM nodes and
1890 // display-related state.
1891
1892 function Display(place, doc, input) {
1893 var d = this;
1894 this.input = input;
1895
1896 // Covers bottom-right square when both scrollbars are present.
1897 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1898 d.scrollbarFiller.setAttribute("cm-not-content", "true");
1899 // Covers bottom of gutter when coverGutterNextToScrollbar is on
1900 // and h scrollbar is present.
1901 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
1902 d.gutterFiller.setAttribute("cm-not-content", "true");
1903 // Will contain the actual code, positioned to cover the viewport.
1904 d.lineDiv = eltP("div", null, "CodeMirror-code");
1905 // Elements are added to these to represent selection and cursors.
1906 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1907 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
1908 // A visibility: hidden element used to find the size of things.
1909 d.measure = elt("div", null, "CodeMirror-measure");
1910 // When lines outside of the viewport are measured, they are drawn in this.
1911 d.lineMeasure = elt("div", null, "CodeMirror-measure");
1912 // Wraps everything that needs to exist inside the vertically-padded coordinate system
1913 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
1914 null, "position: relative; outline: none");
1915 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
1916 // Moved around its parent to cover visible view.
1917 d.mover = elt("div", [lines], null, "position: relative");
1918 // Set to the height of the document, allowing scrolling.
1919 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1920 d.sizerWidth = null;
1921 // Behavior of elts with overflow: auto and padding is
1922 // inconsistent across browsers. This is used to ensure the
1923 // scrollable area is big enough.
1924 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
1925 // Will contain the gutters, if any.
1926 d.gutters = elt("div", null, "CodeMirror-gutters");
1927 d.lineGutter = null;
1928 // Actual scrollable element.
1929 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
1930 d.scroller.setAttribute("tabIndex", "-1");
1931 // The element in which the editor lives.
1932 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
1933
1934 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
1935 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1936 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
1937
1938 if (place) {
1939 if (place.appendChild) { place.appendChild(d.wrapper); }
1940 else { place(d.wrapper); }
1941 }
1942
1943 // Current rendered range (may be bigger than the view window).
1944 d.viewFrom = d.viewTo = doc.first;
1945 d.reportedViewFrom = d.reportedViewTo = doc.first;
1946 // Information about the rendered lines.
1947 d.view = [];
1948 d.renderedView = null;
1949 // Holds info about a single rendered line when it was rendered
1950 // for measurement, while not in view.
1951 d.externalMeasured = null;
1952 // Empty space (in pixels) above the view
1953 d.viewOffset = 0;
1954 d.lastWrapHeight = d.lastWrapWidth = 0;
1955 d.updateLineNumbers = null;
1956
1957 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
1958 d.scrollbarsClipped = false;
1959
1960 // Used to only resize the line number gutter when necessary (when
1961 // the amount of lines crosses a boundary that makes its width change)
1962 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1963 // Set to true when a non-horizontal-scrolling line widget is
1964 // added. As an optimization, line widget aligning is skipped when
1965 // this is false.
1966 d.alignWidgets = false;
1967
1968 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
1969
1970 // Tracks the maximum line length so that the horizontal scrollbar
1971 // can be kept static when scrolling.
1972 d.maxLine = null;
1973 d.maxLineLength = 0;
1974 d.maxLineChanged = false;
1975
1976 // Used for measuring wheel scrolling granularity
1977 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
1978
1979 // True when shift is held down.
1980 d.shift = false;
1981
1982 // Used to track whether anything happened since the context menu
1983 // was opened.
1984 d.selForContextMenu = null;
1985
1986 d.activeTouch = null;
1987
1988 input.init(d);
1989 }
1990
1991 // Find the line object corresponding to the given line number.
1992 function getLine(doc, n) {
1993 n -= doc.first;
1994 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
1995 var chunk = doc;
1996 while (!chunk.lines) {
1997 for (var i = 0;; ++i) {
1998 var child = chunk.children[i], sz = child.chunkSize();
1999 if (n < sz) { chunk = child; break }
2000 n -= sz;
2001 }
2002 }
2003 return chunk.lines[n]
2004 }
2005
2006 // Get the part of a document between two positions, as an array of
2007 // strings.
2008 function getBetween(doc, start, end) {
2009 var out = [], n = start.line;
2010 doc.iter(start.line, end.line + 1, function (line) {
2011 var text = line.text;
2012 if (n == end.line) { text = text.slice(0, end.ch); }
2013 if (n == start.line) { text = text.slice(start.ch); }
2014 out.push(text);
2015 ++n;
2016 });
2017 return out
2018 }
2019 // Get the lines between from and to, as array of strings.
2020 function getLines(doc, from, to) {
2021 var out = [];
2022 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
2023 return out
2024 }
2025
2026 // Update the height of a line, propagating the height change
2027 // upwards to parent nodes.
2028 function updateLineHeight(line, height) {
2029 var diff = height - line.height;
2030 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
2031 }
2032
2033 // Given a line object, find its line number by walking up through
2034 // its parent links.
2035 function lineNo(line) {
2036 if (line.parent == null) { return null }
2037 var cur = line.parent, no = indexOf(cur.lines, line);
2038 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2039 for (var i = 0;; ++i) {
2040 if (chunk.children[i] == cur) { break }
2041 no += chunk.children[i].chunkSize();
2042 }
2043 }
2044 return no + cur.first
2045 }
2046
2047 // Find the line at the given vertical position, using the height
2048 // information in the document tree.
2049 function lineAtHeight(chunk, h) {
2050 var n = chunk.first;
2051 outer: do {
2052 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
2053 var child = chunk.children[i$1], ch = child.height;
2054 if (h < ch) { chunk = child; continue outer }
2055 h -= ch;
2056 n += child.chunkSize();
2057 }
2058 return n
2059 } while (!chunk.lines)
2060 var i = 0;
2061 for (; i < chunk.lines.length; ++i) {
2062 var line = chunk.lines[i], lh = line.height;
2063 if (h < lh) { break }
2064 h -= lh;
2065 }
2066 return n + i
2067 }
2068
2069 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
2070
2071 function lineNumberFor(options, i) {
2072 return String(options.lineNumberFormatter(i + options.firstLineNumber))
2073 }
2074
2075 // A Pos instance represents a position within the text.
2076 function Pos(line, ch, sticky) {
2077 if ( sticky === void 0 ) sticky = null;
2078
2079 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
2080 this.line = line;
2081 this.ch = ch;
2082 this.sticky = sticky;
2083 }
2084
2085 // Compare two positions, return 0 if they are the same, a negative
2086 // number when a is less, and a positive number otherwise.
2087 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
2088
2089 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
2090
2091 function copyPos(x) {return Pos(x.line, x.ch)}
2092 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
2093 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
2094
2095 // Most of the external API clips given positions to make sure they
2096 // actually exist within the document.
2097 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
2098 function clipPos(doc, pos) {
2099 if (pos.line < doc.first) { return Pos(doc.first, 0) }
2100 var last = doc.first + doc.size - 1;
2101 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
2102 return clipToLen(pos, getLine(doc, pos.line).text.length)
2103 }
2104 function clipToLen(pos, linelen) {
2105 var ch = pos.ch;
2106 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
2107 else if (ch < 0) { return Pos(pos.line, 0) }
2108 else { return pos }
2109 }
2110 function clipPosArray(doc, array) {
2111 var out = [];
2112 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
2113 return out
2114 }
2115
2116 // Optimize some code when these features are not used.
2117 var sawReadOnlySpans = false;
2118 var sawCollapsedSpans = false;
2119
2120 function seeReadOnlySpans() {
2121 sawReadOnlySpans = true;
2122 }
2123
2124 function seeCollapsedSpans() {
2125 sawCollapsedSpans = true;
2126 }
2127
2128 // TEXTMARKER SPANS
2129
2130 function MarkedSpan(marker, from, to) {
2131 this.marker = marker;
2132 this.from = from; this.to = to;
2133 }
2134
2135 // Search an array of spans for a span matching the given marker.
2136 function getMarkedSpanFor(spans, marker) {
2137 if (spans) { for (var i = 0; i < spans.length; ++i) {
2138 var span = spans[i];
2139 if (span.marker == marker) { return span }
2140 } }
2141 }
2142 // Remove a span from an array, returning undefined if no spans are
2143 // left (we don't store arrays for lines without spans).
2144 function removeMarkedSpan(spans, span) {
2145 var r;
2146 for (var i = 0; i < spans.length; ++i)
2147 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2148 return r
2149 }
2150 // Add a span to a line.
2151 function addMarkedSpan(line, span) {
2152 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2153 span.marker.attachLine(line);
2154 }
2155
2156 // Used for the algorithm that adjusts markers for a change in the
2157 // document. These functions cut an array of spans at a given
2158 // character position, returning an array of remaining chunks (or
2159 // undefined if nothing remains).
2160 function markedSpansBefore(old, startCh, isInsert) {
2161 var nw;
2162 if (old) { for (var i = 0; i < old.length; ++i) {
2163 var span = old[i], marker = span.marker;
2164 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2165 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2166 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));
2167 }
2168 } }
2169 return nw
2170 }
2171 function markedSpansAfter(old, endCh, isInsert) {
2172 var nw;
2173 if (old) { for (var i = 0; i < old.length; ++i) {
2174 var span = old[i], marker = span.marker;
2175 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2176 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2177 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2178 span.to == null ? null : span.to - endCh));
2179 }
2180 } }
2181 return nw
2182 }
2183
2184 // Given a change object, compute the new set of marker spans that
2185 // cover the line in which the change took place. Removes spans
2186 // entirely within the change, reconnects spans belonging to the
2187 // same marker that appear on both sides of the change, and cuts off
2188 // spans partially within the change. Returns an array of span
2189 // arrays with one element for each line in (after) the change.
2190 function stretchSpansOverChange(doc, change) {
2191 if (change.full) { return null }
2192 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2193 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2194 if (!oldFirst && !oldLast) { return null }
2195
2196 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2197 // Get the spans that 'stick out' on both sides
2198 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2199 var last = markedSpansAfter(oldLast, endCh, isInsert);
2200
2201 // Next, merge those two ends
2202 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2203 if (first) {
2204 // Fix up .to properties of first
2205 for (var i = 0; i < first.length; ++i) {
2206 var span = first[i];
2207 if (span.to == null) {
2208 var found = getMarkedSpanFor(last, span.marker);
2209 if (!found) { span.to = startCh; }
2210 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2211 }
2212 }
2213 }
2214 if (last) {
2215 // Fix up .from in last (or move them into first in case of sameLine)
2216 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2217 var span$1 = last[i$1];
2218 if (span$1.to != null) { span$1.to += offset; }
2219 if (span$1.from == null) {
2220 var found$1 = getMarkedSpanFor(first, span$1.marker);
2221 if (!found$1) {
2222 span$1.from = offset;
2223 if (sameLine) { (first || (first = [])).push(span$1); }
2224 }
2225 } else {
2226 span$1.from += offset;
2227 if (sameLine) { (first || (first = [])).push(span$1); }
2228 }
2229 }
2230 }
2231 // Make sure we didn't create any zero-length spans
2232 if (first) { first = clearEmptySpans(first); }
2233 if (last && last != first) { last = clearEmptySpans(last); }
2234
2235 var newMarkers = [first];
2236 if (!sameLine) {
2237 // Fill gap with whole-line-spans
2238 var gap = change.text.length - 2, gapMarkers;
2239 if (gap > 0 && first)
2240 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2241 { if (first[i$2].to == null)
2242 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2243 for (var i$3 = 0; i$3 < gap; ++i$3)
2244 { newMarkers.push(gapMarkers); }
2245 newMarkers.push(last);
2246 }
2247 return newMarkers
2248 }
2249
2250 // Remove spans that are empty and don't have a clearWhenEmpty
2251 // option of false.
2252 function clearEmptySpans(spans) {
2253 for (var i = 0; i < spans.length; ++i) {
2254 var span = spans[i];
2255 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
2256 { spans.splice(i--, 1); }
2257 }
2258 if (!spans.length) { return null }
2259 return spans
2260 }
2261
2262 // Used to 'clip' out readOnly ranges when making a change.
2263 function removeReadOnlyRanges(doc, from, to) {
2264 var markers = null;
2265 doc.iter(from.line, to.line + 1, function (line) {
2266 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
2267 var mark = line.markedSpans[i].marker;
2268 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
2269 { (markers || (markers = [])).push(mark); }
2270 } }
2271 });
2272 if (!markers) { return null }
2273 var parts = [{from: from, to: to}];
2274 for (var i = 0; i < markers.length; ++i) {
2275 var mk = markers[i], m = mk.find(0);
2276 for (var j = 0; j < parts.length; ++j) {
2277 var p = parts[j];
2278 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
2279 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
2280 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
2281 { newParts.push({from: p.from, to: m.from}); }
2282 if (dto > 0 || !mk.inclusiveRight && !dto)
2283 { newParts.push({from: m.to, to: p.to}); }
2284 parts.splice.apply(parts, newParts);
2285 j += newParts.length - 3;
2286 }
2287 }
2288 return parts
2289 }
2290
2291 // Connect or disconnect spans from a line.
2292 function detachMarkedSpans(line) {
2293 var spans = line.markedSpans;
2294 if (!spans) { return }
2295 for (var i = 0; i < spans.length; ++i)
2296 { spans[i].marker.detachLine(line); }
2297 line.markedSpans = null;
2298 }
2299 function attachMarkedSpans(line, spans) {
2300 if (!spans) { return }
2301 for (var i = 0; i < spans.length; ++i)
2302 { spans[i].marker.attachLine(line); }
2303 line.markedSpans = spans;
2304 }
2305
2306 // Helpers used when computing which overlapping collapsed span
2307 // counts as the larger one.
2308 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
2309 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
2310
2311 // Returns a number indicating which of two overlapping collapsed
2312 // spans is larger (and thus includes the other). Falls back to
2313 // comparing ids when the spans cover exactly the same range.
2314 function compareCollapsedMarkers(a, b) {
2315 var lenDiff = a.lines.length - b.lines.length;
2316 if (lenDiff != 0) { return lenDiff }
2317 var aPos = a.find(), bPos = b.find();
2318 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
2319 if (fromCmp) { return -fromCmp }
2320 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
2321 if (toCmp) { return toCmp }
2322 return b.id - a.id
2323 }
2324
2325 // Find out whether a line ends or starts in a collapsed span. If
2326 // so, return the marker for that span.
2327 function collapsedSpanAtSide(line, start) {
2328 var sps = sawCollapsedSpans && line.markedSpans, found;
2329 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2330 sp = sps[i];
2331 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
2332 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
2333 { found = sp.marker; }
2334 } }
2335 return found
2336 }
2337 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
2338 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
2339
2340 // Test whether there exists a collapsed span that partially
2341 // overlaps (covers the start or end, but not both) of a new span.
2342 // Such overlap is not allowed.
2343 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
2344 var line = getLine(doc, lineNo$$1);
2345 var sps = sawCollapsedSpans && line.markedSpans;
2346 if (sps) { for (var i = 0; i < sps.length; ++i) {
2347 var sp = sps[i];
2348 if (!sp.marker.collapsed) { continue }
2349 var found = sp.marker.find(0);
2350 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
2351 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
2352 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
2353 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
2354 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
2355 { return true }
2356 } }
2357 }
2358
2359 // A visual line is a line as drawn on the screen. Folding, for
2360 // example, can cause multiple logical lines to appear on the same
2361 // visual line. This finds the start of the visual line that the
2362 // given line is part of (usually that is the line itself).
2363 function visualLine(line) {
2364 var merged;
2365 while (merged = collapsedSpanAtStart(line))
2366 { line = merged.find(-1, true).line; }
2367 return line
2368 }
2369
2370 function visualLineEnd(line) {
2371 var merged;
2372 while (merged = collapsedSpanAtEnd(line))
2373 { line = merged.find(1, true).line; }
2374 return line
2375 }
2376
2377 // Returns an array of logical lines that continue the visual line
2378 // started by the argument, or undefined if there are no such lines.
2379 function visualLineContinued(line) {
2380 var merged, lines;
2381 while (merged = collapsedSpanAtEnd(line)) {
2382 line = merged.find(1, true).line
2383 ;(lines || (lines = [])).push(line);
2384 }
2385 return lines
2386 }
2387
2388 // Get the line number of the start of the visual line that the
2389 // given line number is part of.
2390 function visualLineNo(doc, lineN) {
2391 var line = getLine(doc, lineN), vis = visualLine(line);
2392 if (line == vis) { return lineN }
2393 return lineNo(vis)
2394 }
2395
2396 // Get the line number of the start of the next visual line after
2397 // the given line.
2398 function visualLineEndNo(doc, lineN) {
2399 if (lineN > doc.lastLine()) { return lineN }
2400 var line = getLine(doc, lineN), merged;
2401 if (!lineIsHidden(doc, line)) { return lineN }
2402 while (merged = collapsedSpanAtEnd(line))
2403 { line = merged.find(1, true).line; }
2404 return lineNo(line) + 1
2405 }
2406
2407 // Compute whether a line is hidden. Lines count as hidden when they
2408 // are part of a visual line that starts with another line, or when
2409 // they are entirely covered by collapsed, non-widget span.
2410 function lineIsHidden(doc, line) {
2411 var sps = sawCollapsedSpans && line.markedSpans;
2412 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2413 sp = sps[i];
2414 if (!sp.marker.collapsed) { continue }
2415 if (sp.from == null) { return true }
2416 if (sp.marker.widgetNode) { continue }
2417 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
2418 { return true }
2419 } }
2420 }
2421 function lineIsHiddenInner(doc, line, span) {
2422 if (span.to == null) {
2423 var end = span.marker.find(1, true);
2424 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
2425 }
2426 if (span.marker.inclusiveRight && span.to == line.text.length)
2427 { return true }
2428 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
2429 sp = line.markedSpans[i];
2430 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
2431 (sp.to == null || sp.to != span.from) &&
2432 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
2433 lineIsHiddenInner(doc, line, sp)) { return true }
2434 }
2435 }
2436
2437 // Find the height above the given line.
2438 function heightAtLine(lineObj) {
2439 lineObj = visualLine(lineObj);
2440
2441 var h = 0, chunk = lineObj.parent;
2442 for (var i = 0; i < chunk.lines.length; ++i) {
2443 var line = chunk.lines[i];
2444 if (line == lineObj) { break }
2445 else { h += line.height; }
2446 }
2447 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
2448 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
2449 var cur = p.children[i$1];
2450 if (cur == chunk) { break }
2451 else { h += cur.height; }
2452 }
2453 }
2454 return h
2455 }
2456
2457 // Compute the character length of a line, taking into account
2458 // collapsed ranges (see markText) that might hide parts, and join
2459 // other lines onto it.
2460 function lineLength(line) {
2461 if (line.height == 0) { return 0 }
2462 var len = line.text.length, merged, cur = line;
2463 while (merged = collapsedSpanAtStart(cur)) {
2464 var found = merged.find(0, true);
2465 cur = found.from.line;
2466 len += found.from.ch - found.to.ch;
2467 }
2468 cur = line;
2469 while (merged = collapsedSpanAtEnd(cur)) {
2470 var found$1 = merged.find(0, true);
2471 len -= cur.text.length - found$1.from.ch;
2472 cur = found$1.to.line;
2473 len += cur.text.length - found$1.to.ch;
2474 }
2475 return len
2476 }
2477
2478 // Find the longest line in the document.
2479 function findMaxLine(cm) {
2480 var d = cm.display, doc = cm.doc;
2481 d.maxLine = getLine(doc, doc.first);
2482 d.maxLineLength = lineLength(d.maxLine);
2483 d.maxLineChanged = true;
2484 doc.iter(function (line) {
2485 var len = lineLength(line);
2486 if (len > d.maxLineLength) {
2487 d.maxLineLength = len;
2488 d.maxLine = line;
2489 }
2490 });
2491 }
2492
2493 // BIDI HELPERS
2494
2495 function iterateBidiSections(order, from, to, f) {
2496 if (!order) { return f(from, to, "ltr", 0) }
2497 var found = false;
2498 for (var i = 0; i < order.length; ++i) {
2499 var part = order[i];
2500 if (part.from < to && part.to > from || from == to && part.to == from) {
2501 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
2502 found = true;
2503 }
2504 }
2505 if (!found) { f(from, to, "ltr"); }
2506 }
2507
2508 var bidiOther = null;
2509 function getBidiPartAt(order, ch, sticky) {
2510 var found;
2511 bidiOther = null;
2512 for (var i = 0; i < order.length; ++i) {
2513 var cur = order[i];
2514 if (cur.from < ch && cur.to > ch) { return i }
2515 if (cur.to == ch) {
2516 if (cur.from != cur.to && sticky == "before") { found = i; }
2517 else { bidiOther = i; }
2518 }
2519 if (cur.from == ch) {
2520 if (cur.from != cur.to && sticky != "before") { found = i; }
2521 else { bidiOther = i; }
2522 }
2523 }
2524 return found != null ? found : bidiOther
2525 }
2526
2527 // Bidirectional ordering algorithm
2528 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
2529 // that this (partially) implements.
2530
2531 // One-char codes used for character types:
2532 // L (L): Left-to-Right
2533 // R (R): Right-to-Left
2534 // r (AL): Right-to-Left Arabic
2535 // 1 (EN): European Number
2536 // + (ES): European Number Separator
2537 // % (ET): European Number Terminator
2538 // n (AN): Arabic Number
2539 // , (CS): Common Number Separator
2540 // m (NSM): Non-Spacing Mark
2541 // b (BN): Boundary Neutral
2542 // s (B): Paragraph Separator
2543 // t (S): Segment Separator
2544 // w (WS): Whitespace
2545 // N (ON): Other Neutrals
2546
2547 // Returns null if characters are ordered as they appear
2548 // (left-to-right), or an array of sections ({from, to, level}
2549 // objects) in the order in which they occur visually.
2550 var bidiOrdering = (function() {
2551 // Character types for codepoints 0 to 0xff
2552 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
2553 // Character types for codepoints 0x600 to 0x6f9
2554 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
2555 function charType(code) {
2556 if (code <= 0xf7) { return lowTypes.charAt(code) }
2557 else if (0x590 <= code && code <= 0x5f4) { return "R" }
2558 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
2559 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
2560 else if (0x2000 <= code && code <= 0x200b) { return "w" }
2561 else if (code == 0x200c) { return "b" }
2562 else { return "L" }
2563 }
2564
2565 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2566 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
2567
2568 function BidiSpan(level, from, to) {
2569 this.level = level;
2570 this.from = from; this.to = to;
2571 }
2572
2573 return function(str, direction) {
2574 var outerType = direction == "ltr" ? "L" : "R";
2575
2576 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
2577 var len = str.length, types = [];
2578 for (var i = 0; i < len; ++i)
2579 { types.push(charType(str.charCodeAt(i))); }
2580
2581 // W1. Examine each non-spacing mark (NSM) in the level run, and
2582 // change the type of the NSM to the type of the previous
2583 // character. If the NSM is at the start of the level run, it will
2584 // get the type of sor.
2585 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2586 var type = types[i$1];
2587 if (type == "m") { types[i$1] = prev; }
2588 else { prev = type; }
2589 }
2590
2591 // W2. Search backwards from each instance of a European number
2592 // until the first strong type (R, L, AL, or sor) is found. If an
2593 // AL is found, change the type of the European number to Arabic
2594 // number.
2595 // W3. Change all ALs to R.
2596 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2597 var type$1 = types[i$2];
2598 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2599 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2600 }
2601
2602 // W4. A single European separator between two European numbers
2603 // changes to a European number. A single common separator between
2604 // two numbers of the same type changes to that type.
2605 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2606 var type$2 = types[i$3];
2607 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2608 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2609 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2610 prev$1 = type$2;
2611 }
2612
2613 // W5. A sequence of European terminators adjacent to European
2614 // numbers changes to all European numbers.
2615 // W6. Otherwise, separators and terminators change to Other
2616 // Neutral.
2617 for (var i$4 = 0; i$4 < len; ++i$4) {
2618 var type$3 = types[i$4];
2619 if (type$3 == ",") { types[i$4] = "N"; }
2620 else if (type$3 == "%") {
2621 var end = (void 0);
2622 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2623 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2624 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2625 i$4 = end - 1;
2626 }
2627 }
2628
2629 // W7. Search backwards from each instance of a European number
2630 // until the first strong type (R, L, or sor) is found. If an L is
2631 // found, then change the type of the European number to L.
2632 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2633 var type$4 = types[i$5];
2634 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2635 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2636 }
2637
2638 // N1. A sequence of neutrals takes the direction of the
2639 // surrounding strong text if the text on both sides has the same
2640 // direction. European and Arabic numbers act as if they were R in
2641 // terms of their influence on neutrals. Start-of-level-run (sor)
2642 // and end-of-level-run (eor) are used at level run boundaries.
2643 // N2. Any remaining neutrals take the embedding direction.
2644 for (var i$6 = 0; i$6 < len; ++i$6) {
2645 if (isNeutral.test(types[i$6])) {
2646 var end$1 = (void 0);
2647 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2648 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2649 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2650 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2651 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2652 i$6 = end$1 - 1;
2653 }
2654 }
2655
2656 // Here we depart from the documented algorithm, in order to avoid
2657 // building up an actual levels array. Since there are only three
2658 // levels (0, 1, 2) in an implementation that doesn't take
2659 // explicit embedding into account, we can build up the order on
2660 // the fly, without following the level-based algorithm.
2661 var order = [], m;
2662 for (var i$7 = 0; i$7 < len;) {
2663 if (countsAsLeft.test(types[i$7])) {
2664 var start = i$7;
2665 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2666 order.push(new BidiSpan(0, start, i$7));
2667 } else {
2668 var pos = i$7, at = order.length;
2669 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2670 for (var j$2 = pos; j$2 < i$7;) {
2671 if (countsAsNum.test(types[j$2])) {
2672 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2673 var nstart = j$2;
2674 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2675 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2676 pos = j$2;
2677 } else { ++j$2; }
2678 }
2679 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2680 }
2681 }
2682 if (direction == "ltr") {
2683 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2684 order[0].from = m[0].length;
2685 order.unshift(new BidiSpan(0, 0, m[0].length));
2686 }
2687 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2688 lst(order).to -= m[0].length;
2689 order.push(new BidiSpan(0, len - m[0].length, len));
2690 }
2691 }
2692
2693 return direction == "rtl" ? order.reverse() : order
2694 }
2695 })();
2696
2697 // Get the bidi ordering for the given line (and cache it). Returns
2698 // false for lines that are fully left-to-right, and an array of
2699 // BidiSpan objects otherwise.
2700 function getOrder(line, direction) {
2701 var order = line.order;
2702 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2703 return order
2704 }
2705
2706 // EVENT HANDLING
2707
2708 // Lightweight event framework. on/off also work on DOM nodes,
2709 // registering native DOM handlers.
2710
2711 var noHandlers = [];
2712
2713 var on = function(emitter, type, f) {
2714 if (emitter.addEventListener) {
2715 emitter.addEventListener(type, f, false);
2716 } else if (emitter.attachEvent) {
2717 emitter.attachEvent("on" + type, f);
2718 } else {
2719 var map$$1 = emitter._handlers || (emitter._handlers = {});
2720 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2721 }
2722 };
2723
2724 function getHandlers(emitter, type) {
2725 return emitter._handlers && emitter._handlers[type] || noHandlers
2726 }
2727
2728 function off(emitter, type, f) {
2729 if (emitter.removeEventListener) {
2730 emitter.removeEventListener(type, f, false);
2731 } else if (emitter.detachEvent) {
2732 emitter.detachEvent("on" + type, f);
2733 } else {
2734 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2735 if (arr) {
2736 var index = indexOf(arr, f);
2737 if (index > -1)
2738 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2739 }
2740 }
2741 }
2742
2743 function signal(emitter, type /*, values...*/) {
2744 var handlers = getHandlers(emitter, type);
2745 if (!handlers.length) { return }
2746 var args = Array.prototype.slice.call(arguments, 2);
2747 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2748 }
2749
2750 // The DOM events that CodeMirror handles can be overridden by
2751 // registering a (non-DOM) handler on the editor for the event name,
2752 // and preventDefault-ing the event in that handler.
2753 function signalDOMEvent(cm, e, override) {
2754 if (typeof e == "string")
2755 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2756 signal(cm, override || e.type, cm, e);
2757 return e_defaultPrevented(e) || e.codemirrorIgnore
2758 }
2759
2760 function signalCursorActivity(cm) {
2761 var arr = cm._handlers && cm._handlers.cursorActivity;
2762 if (!arr) { return }
2763 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2764 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2765 { set.push(arr[i]); } }
2766 }
2767
2768 function hasHandler(emitter, type) {
2769 return getHandlers(emitter, type).length > 0
2770 }
2771
2772 // Add on and off methods to a constructor's prototype, to make
2773 // registering events on such objects more convenient.
2774 function eventMixin(ctor) {
2775 ctor.prototype.on = function(type, f) {on(this, type, f);};
2776 ctor.prototype.off = function(type, f) {off(this, type, f);};
2777 }
2778
2779 // Due to the fact that we still support jurassic IE versions, some
2780 // compatibility wrappers are needed.
2781
2782 function e_preventDefault(e) {
2783 if (e.preventDefault) { e.preventDefault(); }
2784 else { e.returnValue = false; }
2785 }
2786 function e_stopPropagation(e) {
2787 if (e.stopPropagation) { e.stopPropagation(); }
2788 else { e.cancelBubble = true; }
2789 }
2790 function e_defaultPrevented(e) {
2791 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2792 }
2793 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2794
2795 function e_target(e) {return e.target || e.srcElement}
2796 function e_button(e) {
2797 var b = e.which;
2798 if (b == null) {
2799 if (e.button & 1) { b = 1; }
2800 else if (e.button & 2) { b = 3; }
2801 else if (e.button & 4) { b = 2; }
2802 }
2803 if (mac && e.ctrlKey && b == 1) { b = 3; }
2804 return b
2805 }
2806
2807 // Detect drag-and-drop
2808 var dragAndDrop = function() {
2809 // There is *some* kind of drag-and-drop support in IE6-8, but I
2810 // couldn't get it to work yet.
2811 if (ie && ie_version < 9) { return false }
2812 var div = elt('div');
2813 return "draggable" in div || "dragDrop" in div
2814 }();
2815
2816 var zwspSupported;
2817 function zeroWidthElement(measure) {
2818 if (zwspSupported == null) {
2819 var test = elt("span", "\u200b");
2820 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2821 if (measure.firstChild.offsetHeight != 0)
2822 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2823 }
2824 var node = zwspSupported ? elt("span", "\u200b") :
2825 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2826 node.setAttribute("cm-text", "");
2827 return node
2828 }
2829
2830 // Feature-detect IE's crummy client rect reporting for bidi text
2831 var badBidiRects;
2832 function hasBadBidiRects(measure) {
2833 if (badBidiRects != null) { return badBidiRects }
2834 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2835 var r0 = range(txt, 0, 1).getBoundingClientRect();
2836 var r1 = range(txt, 1, 2).getBoundingClientRect();
2837 removeChildren(measure);
2838 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2839 return badBidiRects = (r1.right - r0.right < 3)
2840 }
2841
2842 // See if "".split is the broken IE version, if so, provide an
2843 // alternative way to split lines.
2844 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2845 var pos = 0, result = [], l = string.length;
2846 while (pos <= l) {
2847 var nl = string.indexOf("\n", pos);
2848 if (nl == -1) { nl = string.length; }
2849 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2850 var rt = line.indexOf("\r");
2851 if (rt != -1) {
2852 result.push(line.slice(0, rt));
2853 pos += rt + 1;
2854 } else {
2855 result.push(line);
2856 pos = nl + 1;
2857 }
2858 }
2859 return result
2860 } : function (string) { return string.split(/\r\n?|\n/); };
2861
2862 var hasSelection = window.getSelection ? function (te) {
2863 try { return te.selectionStart != te.selectionEnd }
2864 catch(e) { return false }
2865 } : function (te) {
2866 var range$$1;
2867 try {range$$1 = te.ownerDocument.selection.createRange();}
2868 catch(e) {}
2869 if (!range$$1 || range$$1.parentElement() != te) { return false }
2870 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2871 };
2872
2873 var hasCopyEvent = (function () {
2874 var e = elt("div");
2875 if ("oncopy" in e) { return true }
2876 e.setAttribute("oncopy", "return;");
2877 return typeof e.oncopy == "function"
2878 })();
2879
2880 var badZoomedRects = null;
2881 function hasBadZoomedRects(measure) {
2882 if (badZoomedRects != null) { return badZoomedRects }
2883 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2884 var normal = node.getBoundingClientRect();
2885 var fromRange = range(node, 0, 1).getBoundingClientRect();
2886 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2887 }
2888
2889 // Known modes, by name and by MIME
2890 var modes = {};
2891 var mimeModes = {};
2892
2893 // Extra arguments are stored as the mode's dependencies, which is
2894 // used by (legacy) mechanisms like loadmode.js to automatically
2895 // load a mode. (Preferred mechanism is the require/define calls.)
2896 function defineMode(name, mode) {
2897 if (arguments.length > 2)
2898 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2899 modes[name] = mode;
2900 }
2901
2902 function defineMIME(mime, spec) {
2903 mimeModes[mime] = spec;
2904 }
2905
2906 // Given a MIME type, a {name, ...options} config object, or a name
2907 // string, return a mode config object.
2908 function resolveMode(spec) {
2909 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2910 spec = mimeModes[spec];
2911 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2912 var found = mimeModes[spec.name];
2913 if (typeof found == "string") { found = {name: found}; }
2914 spec = createObj(found, spec);
2915 spec.name = found.name;
2916 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2917 return resolveMode("application/xml")
2918 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2919 return resolveMode("application/json")
2920 }
2921 if (typeof spec == "string") { return {name: spec} }
2922 else { return spec || {name: "null"} }
2923 }
2924
2925 // Given a mode spec (anything that resolveMode accepts), find and
2926 // initialize an actual mode object.
2927 function getMode(options, spec) {
2928 spec = resolveMode(spec);
2929 var mfactory = modes[spec.name];
2930 if (!mfactory) { return getMode(options, "text/plain") }
2931 var modeObj = mfactory(options, spec);
2932 if (modeExtensions.hasOwnProperty(spec.name)) {
2933 var exts = modeExtensions[spec.name];
2934 for (var prop in exts) {
2935 if (!exts.hasOwnProperty(prop)) { continue }
2936 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2937 modeObj[prop] = exts[prop];
2938 }
2939 }
2940 modeObj.name = spec.name;
2941 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2942 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2943 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2944
2945 return modeObj
2946 }
2947
2948 // This can be used to attach properties to mode objects from
2949 // outside the actual mode definition.
2950 var modeExtensions = {};
2951 function extendMode(mode, properties) {
2952 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2953 copyObj(properties, exts);
2954 }
2955
2956 function copyState(mode, state) {
2957 if (state === true) { return state }
2958 if (mode.copyState) { return mode.copyState(state) }
2959 var nstate = {};
2960 for (var n in state) {
2961 var val = state[n];
2962 if (val instanceof Array) { val = val.concat([]); }
2963 nstate[n] = val;
2964 }
2965 return nstate
2966 }
2967
2968 // Given a mode and a state (for that mode), find the inner mode and
2969 // state at the position that the state refers to.
2970 function innerMode(mode, state) {
2971 var info;
2972 while (mode.innerMode) {
2973 info = mode.innerMode(state);
2974 if (!info || info.mode == mode) { break }
2975 state = info.state;
2976 mode = info.mode;
2977 }
2978 return info || {mode: mode, state: state}
2979 }
2980
2981 function startState(mode, a1, a2) {
2982 return mode.startState ? mode.startState(a1, a2) : true
2983 }
2984
2985 // STRING STREAM
2986
2987 // Fed to the mode parsers, provides helper functions to make
2988 // parsers more succinct.
2989
2990 var StringStream = function(string, tabSize, lineOracle) {
2991 this.pos = this.start = 0;
2992 this.string = string;
2993 this.tabSize = tabSize || 8;
2994 this.lastColumnPos = this.lastColumnValue = 0;
2995 this.lineStart = 0;
2996 this.lineOracle = lineOracle;
2997 };
2998
2999 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
3000 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
3001 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
3002 StringStream.prototype.next = function () {
3003 if (this.pos < this.string.length)
3004 { return this.string.charAt(this.pos++) }
3005 };
3006 StringStream.prototype.eat = function (match) {
3007 var ch = this.string.charAt(this.pos);
3008 var ok;
3009 if (typeof match == "string") { ok = ch == match; }
3010 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
3011 if (ok) {++this.pos; return ch}
3012 };
3013 StringStream.prototype.eatWhile = function (match) {
3014 var start = this.pos;
3015 while (this.eat(match)){}
3016 return this.pos > start
3017 };
3018 StringStream.prototype.eatSpace = function () {
3019 var this$1 = this;
3020
3021 var start = this.pos;
3022 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
3023 return this.pos > start
3024 };
3025 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
3026 StringStream.prototype.skipTo = function (ch) {
3027 var found = this.string.indexOf(ch, this.pos);
3028 if (found > -1) {this.pos = found; return true}
3029 };
3030 StringStream.prototype.backUp = function (n) {this.pos -= n;};
3031 StringStream.prototype.column = function () {
3032 if (this.lastColumnPos < this.start) {
3033 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3034 this.lastColumnPos = this.start;
3035 }
3036 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3037 };
3038 StringStream.prototype.indentation = function () {
3039 return countColumn(this.string, null, this.tabSize) -
3040 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3041 };
3042 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
3043 if (typeof pattern == "string") {
3044 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
3045 var substr = this.string.substr(this.pos, pattern.length);
3046 if (cased(substr) == cased(pattern)) {
3047 if (consume !== false) { this.pos += pattern.length; }
3048 return true
3049 }
3050 } else {
3051 var match = this.string.slice(this.pos).match(pattern);
3052 if (match && match.index > 0) { return null }
3053 if (match && consume !== false) { this.pos += match[0].length; }
3054 return match
3055 }
3056 };
3057 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
3058 StringStream.prototype.hideFirstChars = function (n, inner) {
3059 this.lineStart += n;
3060 try { return inner() }
3061 finally { this.lineStart -= n; }
3062 };
3063 StringStream.prototype.lookAhead = function (n) {
3064 var oracle = this.lineOracle;
3065 return oracle && oracle.lookAhead(n)
3066 };
3067 StringStream.prototype.baseToken = function () {
3068 var oracle = this.lineOracle;
3069 return oracle && oracle.baseToken(this.pos)
3070 };
3071
3072 var SavedContext = function(state, lookAhead) {
3073 this.state = state;
3074 this.lookAhead = lookAhead;
3075 };
3076
3077 var Context = function(doc, state, line, lookAhead) {
3078 this.state = state;
3079 this.doc = doc;
3080 this.line = line;
3081 this.maxLookAhead = lookAhead || 0;
3082 this.baseTokens = null;
3083 this.baseTokenPos = 1;
3084 };
3085
3086 Context.prototype.lookAhead = function (n) {
3087 var line = this.doc.getLine(this.line + n);
3088 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
3089 return line
3090 };
3091
3092 Context.prototype.baseToken = function (n) {
3093 var this$1 = this;
3094
3095 if (!this.baseTokens) { return null }
3096 while (this.baseTokens[this.baseTokenPos] <= n)
3097 { this$1.baseTokenPos += 2; }
3098 var type = this.baseTokens[this.baseTokenPos + 1];
3099 return {type: type && type.replace(/( |^)overlay .*/, ""),
3100 size: this.baseTokens[this.baseTokenPos] - n}
3101 };
3102
3103 Context.prototype.nextLine = function () {
3104 this.line++;
3105 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
3106 };
3107
3108 Context.fromSaved = function (doc, saved, line) {
3109 if (saved instanceof SavedContext)
3110 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
3111 else
3112 { return new Context(doc, copyState(doc.mode, saved), line) }
3113 };
3114
3115 Context.prototype.save = function (copy) {
3116 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
3117 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
3118 };
3119
3120
3121 // Compute a style array (an array starting with a mode generation
3122 // -- for invalidation -- followed by pairs of end positions and
3123 // style strings), which is used to highlight the tokens on the
3124 // line.
3125 function highlightLine(cm, line, context, forceToEnd) {
3126 // A styles array always starts with a number identifying the
3127 // mode/overlays that it is based on (for easy invalidation).
3128 var st = [cm.state.modeGen], lineClasses = {};
3129 // Compute the base array of styles
3130 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
3131 lineClasses, forceToEnd);
3132 var state = context.state;
3133
3134 // Run overlays, adjust style array.
3135 var loop = function ( o ) {
3136 context.baseTokens = st;
3137 var overlay = cm.state.overlays[o], i = 1, at = 0;
3138 context.state = true;
3139 runMode(cm, line.text, overlay.mode, context, function (end, style) {
3140 var start = i;
3141 // Ensure there's a token end at the current position, and that i points at it
3142 while (at < end) {
3143 var i_end = st[i];
3144 if (i_end > end)
3145 { st.splice(i, 1, end, st[i+1], i_end); }
3146 i += 2;
3147 at = Math.min(end, i_end);
3148 }
3149 if (!style) { return }
3150 if (overlay.opaque) {
3151 st.splice(start, i - start, end, "overlay " + style);
3152 i = start + 2;
3153 } else {
3154 for (; start < i; start += 2) {
3155 var cur = st[start+1];
3156 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
3157 }
3158 }
3159 }, lineClasses);
3160 context.state = state;
3161 context.baseTokens = null;
3162 context.baseTokenPos = 1;
3163 };
3164
3165 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
3166
3167 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
3168 }
3169
3170 function getLineStyles(cm, line, updateFrontier) {
3171 if (!line.styles || line.styles[0] != cm.state.modeGen) {
3172 var context = getContextBefore(cm, lineNo(line));
3173 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
3174 var result = highlightLine(cm, line, context);
3175 if (resetState) { context.state = resetState; }
3176 line.stateAfter = context.save(!resetState);
3177 line.styles = result.styles;
3178 if (result.classes) { line.styleClasses = result.classes; }
3179 else if (line.styleClasses) { line.styleClasses = null; }
3180 if (updateFrontier === cm.doc.highlightFrontier)
3181 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
3182 }
3183 return line.styles
3184 }
3185
3186 function getContextBefore(cm, n, precise) {
3187 var doc = cm.doc, display = cm.display;
3188 if (!doc.mode.startState) { return new Context(doc, true, n) }
3189 var start = findStartLine(cm, n, precise);
3190 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
3191 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
3192
3193 doc.iter(start, n, function (line) {
3194 processLine(cm, line.text, context);
3195 var pos = context.line;
3196 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
3197 context.nextLine();
3198 });
3199 if (precise) { doc.modeFrontier = context.line; }
3200 return context
3201 }
3202
3203 // Lightweight form of highlight -- proceed over this line and
3204 // update state, but don't save a style array. Used for lines that
3205 // aren't currently visible.
3206 function processLine(cm, text, context, startAt) {
3207 var mode = cm.doc.mode;
3208 var stream = new StringStream(text, cm.options.tabSize, context);
3209 stream.start = stream.pos = startAt || 0;
3210 if (text == "") { callBlankLine(mode, context.state); }
3211 while (!stream.eol()) {
3212 readToken(mode, stream, context.state);
3213 stream.start = stream.pos;
3214 }
3215 }
3216
3217 function callBlankLine(mode, state) {
3218 if (mode.blankLine) { return mode.blankLine(state) }
3219 if (!mode.innerMode) { return }
3220 var inner = innerMode(mode, state);
3221 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
3222 }
3223
3224 function readToken(mode, stream, state, inner) {
3225 for (var i = 0; i < 10; i++) {
3226 if (inner) { inner[0] = innerMode(mode, state).mode; }
3227 var style = mode.token(stream, state);
3228 if (stream.pos > stream.start) { return style }
3229 }
3230 throw new Error("Mode " + mode.name + " failed to advance stream.")
3231 }
3232
3233 var Token = function(stream, type, state) {
3234 this.start = stream.start; this.end = stream.pos;
3235 this.string = stream.current();
3236 this.type = type || null;
3237 this.state = state;
3238 };
3239
3240 // Utility for getTokenAt and getLineTokens
3241 function takeToken(cm, pos, precise, asArray) {
3242 var doc = cm.doc, mode = doc.mode, style;
3243 pos = clipPos(doc, pos);
3244 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
3245 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
3246 if (asArray) { tokens = []; }
3247 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
3248 stream.start = stream.pos;
3249 style = readToken(mode, stream, context.state);
3250 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
3251 }
3252 return asArray ? tokens : new Token(stream, style, context.state)
3253 }
3254
3255 function extractLineClasses(type, output) {
3256 if (type) { for (;;) {
3257 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
3258 if (!lineClass) { break }
3259 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
3260 var prop = lineClass[1] ? "bgClass" : "textClass";
3261 if (output[prop] == null)
3262 { output[prop] = lineClass[2]; }
3263 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
3264 { output[prop] += " " + lineClass[2]; }
3265 } }
3266 return type
3267 }
3268
3269 // Run the given mode's parser over a line, calling f for each token.
3270 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
3271 var flattenSpans = mode.flattenSpans;
3272 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
3273 var curStart = 0, curStyle = null;
3274 var stream = new StringStream(text, cm.options.tabSize, context), style;
3275 var inner = cm.options.addModeClass && [null];
3276 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
3277 while (!stream.eol()) {
3278 if (stream.pos > cm.options.maxHighlightLength) {
3279 flattenSpans = false;
3280 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
3281 stream.pos = text.length;
3282 style = null;
3283 } else {
3284 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
3285 }
3286 if (inner) {
3287 var mName = inner[0].name;
3288 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
3289 }
3290 if (!flattenSpans || curStyle != style) {
3291 while (curStart < stream.start) {
3292 curStart = Math.min(stream.start, curStart + 5000);
3293 f(curStart, curStyle);
3294 }
3295 curStyle = style;
3296 }
3297 stream.start = stream.pos;
3298 }
3299 while (curStart < stream.pos) {
3300 // Webkit seems to refuse to render text nodes longer than 57444
3301 // characters, and returns inaccurate measurements in nodes
3302 // starting around 5000 chars.
3303 var pos = Math.min(stream.pos, curStart + 5000);
3304 f(pos, curStyle);
3305 curStart = pos;
3306 }
3307 }
3308
3309 // Finds the line to start with when starting a parse. Tries to
3310 // find a line with a stateAfter, so that it can start with a
3311 // valid state. If that fails, it returns the line with the
3312 // smallest indentation, which tends to need the least context to
3313 // parse correctly.
3314 function findStartLine(cm, n, precise) {
3315 var minindent, minline, doc = cm.doc;
3316 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
3317 for (var search = n; search > lim; --search) {
3318 if (search <= doc.first) { return doc.first }
3319 var line = getLine(doc, search - 1), after = line.stateAfter;
3320 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
3321 { return search }
3322 var indented = countColumn(line.text, null, cm.options.tabSize);
3323 if (minline == null || minindent > indented) {
3324 minline = search - 1;
3325 minindent = indented;
3326 }
3327 }
3328 return minline
3329 }
3330
3331 function retreatFrontier(doc, n) {
3332 doc.modeFrontier = Math.min(doc.modeFrontier, n);
3333 if (doc.highlightFrontier < n - 10) { return }
3334 var start = doc.first;
3335 for (var line = n - 1; line > start; line--) {
3336 var saved = getLine(doc, line).stateAfter;
3337 // change is on 3
3338 // state on line 1 looked ahead 2 -- so saw 3
3339 // test 1 + 2 < 3 should cover this
3340 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
3341 start = line + 1;
3342 break
3343 }
3344 }
3345 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
3346 }
3347
3348 // LINE DATA STRUCTURE
3349
3350 // Line objects. These hold state related to a line, including
3351 // highlighting info (the styles array).
3352 var Line = function(text, markedSpans, estimateHeight) {
3353 this.text = text;
3354 attachMarkedSpans(this, markedSpans);
3355 this.height = estimateHeight ? estimateHeight(this) : 1;
3356 };
3357
3358 Line.prototype.lineNo = function () { return lineNo(this) };
3359 eventMixin(Line);
3360
3361 // Change the content (text, markers) of a line. Automatically
3362 // invalidates cached information and tries to re-estimate the
3363 // line's height.
3364 function updateLine(line, text, markedSpans, estimateHeight) {
3365 line.text = text;
3366 if (line.stateAfter) { line.stateAfter = null; }
3367 if (line.styles) { line.styles = null; }
3368 if (line.order != null) { line.order = null; }
3369 detachMarkedSpans(line);
3370 attachMarkedSpans(line, markedSpans);
3371 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3372 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3373 }
3374
3375 // Detach a line from the document tree and its markers.
3376 function cleanUpLine(line) {
3377 line.parent = null;
3378 detachMarkedSpans(line);
3379 }
3380
3381 // Convert a style as returned by a mode (either null, or a string
3382 // containing one or more styles) to a CSS style. This is cached,
3383 // and also looks for line-wide styles.
3384 var styleToClassCache = {};
3385 var styleToClassCacheWithMode = {};
3386 function interpretTokenStyle(style, options) {
3387 if (!style || /^\s*$/.test(style)) { return null }
3388 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3389 return cache[style] ||
3390 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3391 }
3392
3393 // Render the DOM representation of the text of a line. Also builds
3394 // up a 'line map', which points at the DOM nodes that represent
3395 // specific stretches of text, and is used by the measuring code.
3396 // The returned object contains the DOM node, this map, and
3397 // information about line-wide styles that were set by the mode.
3398 function buildLineContent(cm, lineView) {
3399 // The padding-right forces the element to have a 'border', which
3400 // is needed on Webkit to be able to get line-level bounding
3401 // rectangles for it (in measureChar).
3402 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3403 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3404 col: 0, pos: 0, cm: cm,
3405 trailingSpace: false,
3406 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
3407 lineView.measure = {};
3408
3409 // Iterate over the logical lines that make up this visual line.
3410 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3411 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3412 builder.pos = 0;
3413 builder.addToken = buildToken;
3414 // Optionally wire in some hacks into the token-rendering
3415 // algorithm, to deal with browser quirks.
3416 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3417 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3418 builder.map = [];
3419 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3420 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3421 if (line.styleClasses) {
3422 if (line.styleClasses.bgClass)
3423 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3424 if (line.styleClasses.textClass)
3425 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3426 }
3427
3428 // Ensure at least a single node is present, for measuring.
3429 if (builder.map.length == 0)
3430 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3431
3432 // Store the map and a cache object for the current logical line
3433 if (i == 0) {
3434 lineView.measure.map = builder.map;
3435 lineView.measure.cache = {};
3436 } else {
3437 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3438 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3439 }
3440 }
3441
3442 // See issue #2901
3443 if (webkit) {
3444 var last = builder.content.lastChild;
3445 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3446 { builder.content.className = "cm-tab-wrap-hack"; }
3447 }
3448
3449 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3450 if (builder.pre.className)
3451 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3452
3453 return builder
3454 }
3455
3456 function defaultSpecialCharPlaceholder(ch) {
3457 var token = elt("span", "\u2022", "cm-invalidchar");
3458 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3459 token.setAttribute("aria-label", token.title);
3460 return token
3461 }
3462
3463 // Build up the DOM representation for a single token, and add it to
3464 // the line map. Takes care to render special characters separately.
3465 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
3466 if (!text) { return }
3467 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3468 var special = builder.cm.state.specialChars, mustWrap = false;
3469 var content;
3470 if (!special.test(text)) {
3471 builder.col += text.length;
3472 content = document.createTextNode(displayText);
3473 builder.map.push(builder.pos, builder.pos + text.length, content);
3474 if (ie && ie_version < 9) { mustWrap = true; }
3475 builder.pos += text.length;
3476 } else {
3477 content = document.createDocumentFragment();
3478 var pos = 0;
3479 while (true) {
3480 special.lastIndex = pos;
3481 var m = special.exec(text);
3482 var skipped = m ? m.index - pos : text.length - pos;
3483 if (skipped) {
3484 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3485 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3486 else { content.appendChild(txt); }
3487 builder.map.push(builder.pos, builder.pos + skipped, txt);
3488 builder.col += skipped;
3489 builder.pos += skipped;
3490 }
3491 if (!m) { break }
3492 pos += skipped + 1;
3493 var txt$1 = (void 0);
3494 if (m[0] == "\t") {
3495 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3496 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3497 txt$1.setAttribute("role", "presentation");
3498 txt$1.setAttribute("cm-text", "\t");
3499 builder.col += tabWidth;
3500 } else if (m[0] == "\r" || m[0] == "\n") {
3501 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3502 txt$1.setAttribute("cm-text", m[0]);
3503 builder.col += 1;
3504 } else {
3505 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3506 txt$1.setAttribute("cm-text", m[0]);
3507 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3508 else { content.appendChild(txt$1); }
3509 builder.col += 1;
3510 }
3511 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3512 builder.pos++;
3513 }
3514 }
3515 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3516 if (style || startStyle || endStyle || mustWrap || css) {
3517 var fullStyle = style || "";
3518 if (startStyle) { fullStyle += startStyle; }
3519 if (endStyle) { fullStyle += endStyle; }
3520 var token = elt("span", [content], fullStyle, css);
3521 if (title) { token.title = title; }
3522 return builder.content.appendChild(token)
3523 }
3524 builder.content.appendChild(content);
3525 }
3526
3527 function splitSpaces(text, trailingBefore) {
3528 if (text.length > 1 && !/ /.test(text)) { return text }
3529 var spaceBefore = trailingBefore, result = "";
3530 for (var i = 0; i < text.length; i++) {
3531 var ch = text.charAt(i);
3532 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3533 { ch = "\u00a0"; }
3534 result += ch;
3535 spaceBefore = ch == " ";
3536 }
3537 return result
3538 }
3539
3540 // Work around nonsense dimensions being reported for stretches of
3541 // right-to-left text.
3542 function buildTokenBadBidi(inner, order) {
3543 return function (builder, text, style, startStyle, endStyle, title, css) {
3544 style = style ? style + " cm-force-border" : "cm-force-border";
3545 var start = builder.pos, end = start + text.length;
3546 for (;;) {
3547 // Find the part that overlaps with the start of this text
3548 var part = (void 0);
3549 for (var i = 0; i < order.length; i++) {
3550 part = order[i];
3551 if (part.to > start && part.from <= start) { break }
3552 }
3553 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
3554 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
3555 startStyle = null;
3556 text = text.slice(part.to - start);
3557 start = part.to;
3558 }
3559 }
3560 }
3561
3562 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3563 var widget = !ignoreWidget && marker.widgetNode;
3564 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3565 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3566 if (!widget)
3567 { widget = builder.content.appendChild(document.createElement("span")); }
3568 widget.setAttribute("cm-marker", marker.id);
3569 }
3570 if (widget) {
3571 builder.cm.display.input.setUneditable(widget);
3572 builder.content.appendChild(widget);
3573 }
3574 builder.pos += size;
3575 builder.trailingSpace = false;
3576 }
3577
3578 // Outputs a number of spans to make up a line, taking highlighting
3579 // and marked text into account.
3580 function insertLineContent(line, builder, styles) {
3581 var spans = line.markedSpans, allText = line.text, at = 0;
3582 if (!spans) {
3583 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3584 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3585 return
3586 }
3587
3588 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3589 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
3590 for (;;) {
3591 if (nextChange == pos) { // Update current marker set
3592 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
3593 collapsed = null; nextChange = Infinity;
3594 var foundBookmarks = [], endStyles = (void 0);
3595 for (var j = 0; j < spans.length; ++j) {
3596 var sp = spans[j], m = sp.marker;
3597 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3598 foundBookmarks.push(m);
3599 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3600 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3601 nextChange = sp.to;
3602 spanEndStyle = "";
3603 }
3604 if (m.className) { spanStyle += " " + m.className; }
3605 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3606 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3607 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3608 if (m.title && !title) { title = m.title; }
3609 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3610 { collapsed = sp; }
3611 } else if (sp.from > pos && nextChange > sp.from) {
3612 nextChange = sp.from;
3613 }
3614 }
3615 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3616 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3617
3618 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3619 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3620 if (collapsed && (collapsed.from || 0) == pos) {
3621 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3622 collapsed.marker, collapsed.from == null);
3623 if (collapsed.to == null) { return }
3624 if (collapsed.to == pos) { collapsed = false; }
3625 }
3626 }
3627 if (pos >= len) { break }
3628
3629 var upto = Math.min(len, nextChange);
3630 while (true) {
3631 if (text) {
3632 var end = pos + text.length;
3633 if (!collapsed) {
3634 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3635 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3636 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
3637 }
3638 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3639 pos = end;
3640 spanStartStyle = "";
3641 }
3642 text = allText.slice(at, at = styles[i++]);
3643 style = interpretTokenStyle(styles[i++], builder.cm.options);
3644 }
3645 }
3646 }
3647
3648
3649 // These objects are used to represent the visible (currently drawn)
3650 // part of the document. A LineView may correspond to multiple
3651 // logical lines, if those are connected by collapsed ranges.
3652 function LineView(doc, line, lineN) {
3653 // The starting line
3654 this.line = line;
3655 // Continuing lines, if any
3656 this.rest = visualLineContinued(line);
3657 // Number of logical lines in this visual line
3658 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3659 this.node = this.text = null;
3660 this.hidden = lineIsHidden(doc, line);
3661 }
3662
3663 // Create a range of LineView objects for the given lines.
3664 function buildViewArray(cm, from, to) {
3665 var array = [], nextPos;
3666 for (var pos = from; pos < to; pos = nextPos) {
3667 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3668 nextPos = pos + view.size;
3669 array.push(view);
3670 }
3671 return array
3672 }
3673
3674 var operationGroup = null;
3675
3676 function pushOperation(op) {
3677 if (operationGroup) {
3678 operationGroup.ops.push(op);
3679 } else {
3680 op.ownsGroup = operationGroup = {
3681 ops: [op],
3682 delayedCallbacks: []
3683 };
3684 }
3685 }
3686
3687 function fireCallbacksForOps(group) {
3688 // Calls delayed callbacks and cursorActivity handlers until no
3689 // new ones appear
3690 var callbacks = group.delayedCallbacks, i = 0;
3691 do {
3692 for (; i < callbacks.length; i++)
3693 { callbacks[i].call(null); }
3694 for (var j = 0; j < group.ops.length; j++) {
3695 var op = group.ops[j];
3696 if (op.cursorActivityHandlers)
3697 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3698 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3699 }
3700 } while (i < callbacks.length)
3701 }
3702
3703 function finishOperation(op, endCb) {
3704 var group = op.ownsGroup;
3705 if (!group) { return }
3706
3707 try { fireCallbacksForOps(group); }
3708 finally {
3709 operationGroup = null;
3710 endCb(group);
3711 }
3712 }
3713
3714 var orphanDelayedCallbacks = null;
3715
3716 // Often, we want to signal events at a point where we are in the
3717 // middle of some work, but don't want the handler to start calling
3718 // other methods on the editor, which might be in an inconsistent
3719 // state or simply not expect any other events to happen.
3720 // signalLater looks whether there are any handlers, and schedules
3721 // them to be executed when the last operation ends, or, if no
3722 // operation is active, when a timeout fires.
3723 function signalLater(emitter, type /*, values...*/) {
3724 var arr = getHandlers(emitter, type);
3725 if (!arr.length) { return }
3726 var args = Array.prototype.slice.call(arguments, 2), list;
3727 if (operationGroup) {
3728 list = operationGroup.delayedCallbacks;
3729 } else if (orphanDelayedCallbacks) {
3730 list = orphanDelayedCallbacks;
3731 } else {
3732 list = orphanDelayedCallbacks = [];
3733 setTimeout(fireOrphanDelayed, 0);
3734 }
3735 var loop = function ( i ) {
3736 list.push(function () { return arr[i].apply(null, args); });
3737 };
3738
3739 for (var i = 0; i < arr.length; ++i)
3740 loop( i );
3741 }
3742
3743 function fireOrphanDelayed() {
3744 var delayed = orphanDelayedCallbacks;
3745 orphanDelayedCallbacks = null;
3746 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3747 }
3748
3749 // When an aspect of a line changes, a string is added to
3750 // lineView.changes. This updates the relevant part of the line's
3751 // DOM structure.
3752 function updateLineForChanges(cm, lineView, lineN, dims) {
3753 for (var j = 0; j < lineView.changes.length; j++) {
3754 var type = lineView.changes[j];
3755 if (type == "text") { updateLineText(cm, lineView); }
3756 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3757 else if (type == "class") { updateLineClasses(cm, lineView); }
3758 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3759 }
3760 lineView.changes = null;
3761 }
3762
3763 // Lines with gutter elements, widgets or a background class need to
3764 // be wrapped, and have the extra elements added to the wrapper div
3765 function ensureLineWrapped(lineView) {
3766 if (lineView.node == lineView.text) {
3767 lineView.node = elt("div", null, null, "position: relative");
3768 if (lineView.text.parentNode)
3769 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3770 lineView.node.appendChild(lineView.text);
3771 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3772 }
3773 return lineView.node
3774 }
3775
3776 function updateLineBackground(cm, lineView) {
3777 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3778 if (cls) { cls += " CodeMirror-linebackground"; }
3779 if (lineView.background) {
3780 if (cls) { lineView.background.className = cls; }
3781 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3782 } else if (cls) {
3783 var wrap = ensureLineWrapped(lineView);
3784 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3785 cm.display.input.setUneditable(lineView.background);
3786 }
3787 }
3788
3789 // Wrapper around buildLineContent which will reuse the structure
3790 // in display.externalMeasured when possible.
3791 function getLineContent(cm, lineView) {
3792 var ext = cm.display.externalMeasured;
3793 if (ext && ext.line == lineView.line) {
3794 cm.display.externalMeasured = null;
3795 lineView.measure = ext.measure;
3796 return ext.built
3797 }
3798 return buildLineContent(cm, lineView)
3799 }
3800
3801 // Redraw the line's text. Interacts with the background and text
3802 // classes because the mode may output tokens that influence these
3803 // classes.
3804 function updateLineText(cm, lineView) {
3805 var cls = lineView.text.className;
3806 var built = getLineContent(cm, lineView);
3807 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3808 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3809 lineView.text = built.pre;
3810 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3811 lineView.bgClass = built.bgClass;
3812 lineView.textClass = built.textClass;
3813 updateLineClasses(cm, lineView);
3814 } else if (cls) {
3815 lineView.text.className = cls;
3816 }
3817 }
3818
3819 function updateLineClasses(cm, lineView) {
3820 updateLineBackground(cm, lineView);
3821 if (lineView.line.wrapClass)
3822 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3823 else if (lineView.node != lineView.text)
3824 { lineView.node.className = ""; }
3825 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3826 lineView.text.className = textClass || "";
3827 }
3828
3829 function updateLineGutter(cm, lineView, lineN, dims) {
3830 if (lineView.gutter) {
3831 lineView.node.removeChild(lineView.gutter);
3832 lineView.gutter = null;
3833 }
3834 if (lineView.gutterBackground) {
3835 lineView.node.removeChild(lineView.gutterBackground);
3836 lineView.gutterBackground = null;
3837 }
3838 if (lineView.line.gutterClass) {
3839 var wrap = ensureLineWrapped(lineView);
3840 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3841 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3842 cm.display.input.setUneditable(lineView.gutterBackground);
3843 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3844 }
3845 var markers = lineView.line.gutterMarkers;
3846 if (cm.options.lineNumbers || markers) {
3847 var wrap$1 = ensureLineWrapped(lineView);
3848 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3849 cm.display.input.setUneditable(gutterWrap);
3850 wrap$1.insertBefore(gutterWrap, lineView.text);
3851 if (lineView.line.gutterClass)
3852 { gutterWrap.className += " " + lineView.line.gutterClass; }
3853 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3854 { lineView.lineNumber = gutterWrap.appendChild(
3855 elt("div", lineNumberFor(cm.options, lineN),
3856 "CodeMirror-linenumber CodeMirror-gutter-elt",
3857 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3858 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
3859 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3860 if (found)
3861 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3862 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3863 } }
3864 }
3865 }
3866
3867 function updateLineWidgets(cm, lineView, dims) {
3868 if (lineView.alignable) { lineView.alignable = null; }
3869 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3870 next = node.nextSibling;
3871 if (node.className == "CodeMirror-linewidget")
3872 { lineView.node.removeChild(node); }
3873 }
3874 insertLineWidgets(cm, lineView, dims);
3875 }
3876
3877 // Build a line's DOM representation from scratch
3878 function buildLineElement(cm, lineView, lineN, dims) {
3879 var built = getLineContent(cm, lineView);
3880 lineView.text = lineView.node = built.pre;
3881 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3882 if (built.textClass) { lineView.textClass = built.textClass; }
3883
3884 updateLineClasses(cm, lineView);
3885 updateLineGutter(cm, lineView, lineN, dims);
3886 insertLineWidgets(cm, lineView, dims);
3887 return lineView.node
3888 }
3889
3890 // A lineView may contain multiple logical lines (when merged by
3891 // collapsed spans). The widgets for all of them need to be drawn.
3892 function insertLineWidgets(cm, lineView, dims) {
3893 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3894 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3895 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3896 }
3897
3898 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3899 if (!line.widgets) { return }
3900 var wrap = ensureLineWrapped(lineView);
3901 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3902 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3903 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3904 positionLineWidget(widget, node, lineView, dims);
3905 cm.display.input.setUneditable(node);
3906 if (allowAbove && widget.above)
3907 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3908 else
3909 { wrap.appendChild(node); }
3910 signalLater(widget, "redraw");
3911 }
3912 }
3913
3914 function positionLineWidget(widget, node, lineView, dims) {
3915 if (widget.noHScroll) {
3916 (lineView.alignable || (lineView.alignable = [])).push(node);
3917 var width = dims.wrapperWidth;
3918 node.style.left = dims.fixedPos + "px";
3919 if (!widget.coverGutter) {
3920 width -= dims.gutterTotalWidth;
3921 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3922 }
3923 node.style.width = width + "px";
3924 }
3925 if (widget.coverGutter) {
3926 node.style.zIndex = 5;
3927 node.style.position = "relative";
3928 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3929 }
3930 }
3931
3932 function widgetHeight(widget) {
3933 if (widget.height != null) { return widget.height }
3934 var cm = widget.doc.cm;
3935 if (!cm) { return 0 }
3936 if (!contains(document.body, widget.node)) {
3937 var parentStyle = "position: relative;";
3938 if (widget.coverGutter)
3939 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3940 if (widget.noHScroll)
3941 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3942 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3943 }
3944 return widget.height = widget.node.parentNode.offsetHeight
3945 }
3946
3947 // Return true when the given mouse event happened in a widget
3948 function eventInWidget(display, e) {
3949 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3950 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3951 (n.parentNode == display.sizer && n != display.mover))
3952 { return true }
3953 }
3954 }
3955
3956 // POSITION MEASUREMENT
3957
3958 function paddingTop(display) {return display.lineSpace.offsetTop}
3959 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3960 function paddingH(display) {
3961 if (display.cachedPaddingH) { return display.cachedPaddingH }
3962 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3963 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3964 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3965 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3966 return data
3967 }
3968
3969 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3970 function displayWidth(cm) {
3971 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3972 }
3973 function displayHeight(cm) {
3974 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3975 }
3976
3977 // Ensure the lineView.wrapping.heights array is populated. This is
3978 // an array of bottom offsets for the lines that make up a drawn
3979 // line. When lineWrapping is on, there might be more than one
3980 // height.
3981 function ensureLineHeights(cm, lineView, rect) {
3982 var wrapping = cm.options.lineWrapping;
3983 var curWidth = wrapping && displayWidth(cm);
3984 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3985 var heights = lineView.measure.heights = [];
3986 if (wrapping) {
3987 lineView.measure.width = curWidth;
3988 var rects = lineView.text.firstChild.getClientRects();
3989 for (var i = 0; i < rects.length - 1; i++) {
3990 var cur = rects[i], next = rects[i + 1];
3991 if (Math.abs(cur.bottom - next.bottom) > 2)
3992 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
3993 }
3994 }
3995 heights.push(rect.bottom - rect.top);
3996 }
3997 }
3998
3999 // Find a line map (mapping character offsets to text nodes) and a
4000 // measurement cache for the given line number. (A line view might
4001 // contain multiple lines when collapsed ranges are present.)
4002 function mapFromLineView(lineView, line, lineN) {
4003 if (lineView.line == line)
4004 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
4005 for (var i = 0; i < lineView.rest.length; i++)
4006 { if (lineView.rest[i] == line)
4007 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
4008 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
4009 { if (lineNo(lineView.rest[i$1]) > lineN)
4010 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
4011 }
4012
4013 // Render a line into the hidden node display.externalMeasured. Used
4014 // when measurement is needed for a line that's not in the viewport.
4015 function updateExternalMeasurement(cm, line) {
4016 line = visualLine(line);
4017 var lineN = lineNo(line);
4018 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
4019 view.lineN = lineN;
4020 var built = view.built = buildLineContent(cm, view);
4021 view.text = built.pre;
4022 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
4023 return view
4024 }
4025
4026 // Get a {top, bottom, left, right} box (in line-local coordinates)
4027 // for a given character.
4028 function measureChar(cm, line, ch, bias) {
4029 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
4030 }
4031
4032 // Find a line view that corresponds to the given line number.
4033 function findViewForLine(cm, lineN) {
4034 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
4035 { return cm.display.view[findViewIndex(cm, lineN)] }
4036 var ext = cm.display.externalMeasured;
4037 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
4038 { return ext }
4039 }
4040
4041 // Measurement can be split in two steps, the set-up work that
4042 // applies to the whole line, and the measurement of the actual
4043 // character. Functions like coordsChar, that need to do a lot of
4044 // measurements in a row, can thus ensure that the set-up work is
4045 // only done once.
4046 function prepareMeasureForLine(cm, line) {
4047 var lineN = lineNo(line);
4048 var view = findViewForLine(cm, lineN);
4049 if (view && !view.text) {
4050 view = null;
4051 } else if (view && view.changes) {
4052 updateLineForChanges(cm, view, lineN, getDimensions(cm));
4053 cm.curOp.forceUpdate = true;
4054 }
4055 if (!view)
4056 { view = updateExternalMeasurement(cm, line); }
4057
4058 var info = mapFromLineView(view, line, lineN);
4059 return {
4060 line: line, view: view, rect: null,
4061 map: info.map, cache: info.cache, before: info.before,
4062 hasHeights: false
4063 }
4064 }
4065
4066 // Given a prepared measurement object, measures the position of an
4067 // actual character (or fetches it from the cache).
4068 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
4069 if (prepared.before) { ch = -1; }
4070 var key = ch + (bias || ""), found;
4071 if (prepared.cache.hasOwnProperty(key)) {
4072 found = prepared.cache[key];
4073 } else {
4074 if (!prepared.rect)
4075 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
4076 if (!prepared.hasHeights) {
4077 ensureLineHeights(cm, prepared.view, prepared.rect);
4078 prepared.hasHeights = true;
4079 }
4080 found = measureCharInner(cm, prepared, ch, bias);
4081 if (!found.bogus) { prepared.cache[key] = found; }
4082 }
4083 return {left: found.left, right: found.right,
4084 top: varHeight ? found.rtop : found.top,
4085 bottom: varHeight ? found.rbottom : found.bottom}
4086 }
4087
4088 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4089
4090 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
4091 var node, start, end, collapse, mStart, mEnd;
4092 // First, search the line map for the text node corresponding to,
4093 // or closest to, the target character.
4094 for (var i = 0; i < map$$1.length; i += 3) {
4095 mStart = map$$1[i];
4096 mEnd = map$$1[i + 1];
4097 if (ch < mStart) {
4098 start = 0; end = 1;
4099 collapse = "left";
4100 } else if (ch < mEnd) {
4101 start = ch - mStart;
4102 end = start + 1;
4103 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
4104 end = mEnd - mStart;
4105 start = end - 1;
4106 if (ch >= mEnd) { collapse = "right"; }
4107 }
4108 if (start != null) {
4109 node = map$$1[i + 2];
4110 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4111 { collapse = bias; }
4112 if (bias == "left" && start == 0)
4113 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
4114 node = map$$1[(i -= 3) + 2];
4115 collapse = "left";
4116 } }
4117 if (bias == "right" && start == mEnd - mStart)
4118 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
4119 node = map$$1[(i += 3) + 2];
4120 collapse = "right";
4121 } }
4122 break
4123 }
4124 }
4125 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4126 }
4127
4128 function getUsefulRect(rects, bias) {
4129 var rect = nullRect;
4130 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4131 if ((rect = rects[i]).left != rect.right) { break }
4132 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4133 if ((rect = rects[i$1]).left != rect.right) { break }
4134 } }
4135 return rect
4136 }
4137
4138 function measureCharInner(cm, prepared, ch, bias) {
4139 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4140 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4141
4142 var rect;
4143 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4144 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4145 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4146 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4147 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4148 { rect = node.parentNode.getBoundingClientRect(); }
4149 else
4150 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4151 if (rect.left || rect.right || start == 0) { break }
4152 end = start;
4153 start = start - 1;
4154 collapse = "right";
4155 }
4156 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4157 } else { // If it is a widget, simply get the box for the whole widget.
4158 if (start > 0) { collapse = bias = "right"; }
4159 var rects;
4160 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4161 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4162 else
4163 { rect = node.getBoundingClientRect(); }
4164 }
4165 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4166 var rSpan = node.parentNode.getClientRects()[0];
4167 if (rSpan)
4168 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4169 else
4170 { rect = nullRect; }
4171 }
4172
4173 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4174 var mid = (rtop + rbot) / 2;
4175 var heights = prepared.view.measure.heights;
4176 var i = 0;
4177 for (; i < heights.length - 1; i++)
4178 { if (mid < heights[i]) { break } }
4179 var top = i ? heights[i - 1] : 0, bot = heights[i];
4180 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4181 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4182 top: top, bottom: bot};
4183 if (!rect.left && !rect.right) { result.bogus = true; }
4184 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4185
4186 return result
4187 }
4188
4189 // Work around problem with bounding client rects on ranges being
4190 // returned incorrectly when zoomed on IE10 and below.
4191 function maybeUpdateRectForZooming(measure, rect) {
4192 if (!window.screen || screen.logicalXDPI == null ||
4193 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4194 { return rect }
4195 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4196 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4197 return {left: rect.left * scaleX, right: rect.right * scaleX,
4198 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4199 }
4200
4201 function clearLineMeasurementCacheFor(lineView) {
4202 if (lineView.measure) {
4203 lineView.measure.cache = {};
4204 lineView.measure.heights = null;
4205 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4206 { lineView.measure.caches[i] = {}; } }
4207 }
4208 }
4209
4210 function clearLineMeasurementCache(cm) {
4211 cm.display.externalMeasure = null;
4212 removeChildren(cm.display.lineMeasure);
4213 for (var i = 0; i < cm.display.view.length; i++)
4214 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4215 }
4216
4217 function clearCaches(cm) {
4218 clearLineMeasurementCache(cm);
4219 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4220 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4221 cm.display.lineNumChars = null;
4222 }
4223
4224 function pageScrollX() {
4225 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4226 // which causes page_Offset and bounding client rects to use
4227 // different reference viewports and invalidate our calculations.
4228 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4229 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4230 }
4231 function pageScrollY() {
4232 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4233 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4234 }
4235
4236 function widgetTopHeight(lineObj) {
4237 var height = 0;
4238 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4239 { height += widgetHeight(lineObj.widgets[i]); } } }
4240 return height
4241 }
4242
4243 // Converts a {top, bottom, left, right} box from line-local
4244 // coordinates into another coordinate system. Context may be one of
4245 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4246 // or "page".
4247 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4248 if (!includeWidgets) {
4249 var height = widgetTopHeight(lineObj);
4250 rect.top += height; rect.bottom += height;
4251 }
4252 if (context == "line") { return rect }
4253 if (!context) { context = "local"; }
4254 var yOff = heightAtLine(lineObj);
4255 if (context == "local") { yOff += paddingTop(cm.display); }
4256 else { yOff -= cm.display.viewOffset; }
4257 if (context == "page" || context == "window") {
4258 var lOff = cm.display.lineSpace.getBoundingClientRect();
4259 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4260 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4261 rect.left += xOff; rect.right += xOff;
4262 }
4263 rect.top += yOff; rect.bottom += yOff;
4264 return rect
4265 }
4266
4267 // Coverts a box from "div" coords to another coordinate system.
4268 // Context may be "window", "page", "div", or "local"./null.
4269 function fromCoordSystem(cm, coords, context) {
4270 if (context == "div") { return coords }
4271 var left = coords.left, top = coords.top;
4272 // First move into "page" coordinate system
4273 if (context == "page") {
4274 left -= pageScrollX();
4275 top -= pageScrollY();
4276 } else if (context == "local" || !context) {
4277 var localBox = cm.display.sizer.getBoundingClientRect();
4278 left += localBox.left;
4279 top += localBox.top;
4280 }
4281
4282 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4283 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4284 }
4285
4286 function charCoords(cm, pos, context, lineObj, bias) {
4287 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4288 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4289 }
4290
4291 // Returns a box for a given cursor position, which may have an
4292 // 'other' property containing the position of the secondary cursor
4293 // on a bidi boundary.
4294 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4295 // and after `char - 1` in writing order of `char - 1`
4296 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4297 // and before `char` in writing order of `char`
4298 // Examples (upper-case letters are RTL, lower-case are LTR):
4299 // Pos(0, 1, ...)
4300 // before after
4301 // ab a|b a|b
4302 // aB a|B aB|
4303 // Ab |Ab A|b
4304 // AB B|A B|A
4305 // Every position after the last character on a line is considered to stick
4306 // to the last character on the line.
4307 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4308 lineObj = lineObj || getLine(cm.doc, pos.line);
4309 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4310 function get(ch, right) {
4311 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4312 if (right) { m.left = m.right; } else { m.right = m.left; }
4313 return intoCoordSystem(cm, lineObj, m, context)
4314 }
4315 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4316 if (ch >= lineObj.text.length) {
4317 ch = lineObj.text.length;
4318 sticky = "before";
4319 } else if (ch <= 0) {
4320 ch = 0;
4321 sticky = "after";
4322 }
4323 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4324
4325 function getBidi(ch, partPos, invert) {
4326 var part = order[partPos], right = part.level == 1;
4327 return get(invert ? ch - 1 : ch, right != invert)
4328 }
4329 var partPos = getBidiPartAt(order, ch, sticky);
4330 var other = bidiOther;
4331 var val = getBidi(ch, partPos, sticky == "before");
4332 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4333 return val
4334 }
4335
4336 // Used to cheaply estimate the coordinates for a position. Used for
4337 // intermediate scroll updates.
4338 function estimateCoords(cm, pos) {
4339 var left = 0;
4340 pos = clipPos(cm.doc, pos);
4341 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4342 var lineObj = getLine(cm.doc, pos.line);
4343 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4344 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4345 }
4346
4347 // Positions returned by coordsChar contain some extra information.
4348 // xRel is the relative x position of the input coordinates compared
4349 // to the found position (so xRel > 0 means the coordinates are to
4350 // the right of the character position, for example). When outside
4351 // is true, that means the coordinates lie outside the line's
4352 // vertical range.
4353 function PosWithInfo(line, ch, sticky, outside, xRel) {
4354 var pos = Pos(line, ch, sticky);
4355 pos.xRel = xRel;
4356 if (outside) { pos.outside = true; }
4357 return pos
4358 }
4359
4360 // Compute the character position closest to the given coordinates.
4361 // Input must be lineSpace-local ("div" coordinate system).
4362 function coordsChar(cm, x, y) {
4363 var doc = cm.doc;
4364 y += cm.display.viewOffset;
4365 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4366 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4367 if (lineN > last)
4368 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4369 if (x < 0) { x = 0; }
4370
4371 var lineObj = getLine(doc, lineN);
4372 for (;;) {
4373 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4374 var merged = collapsedSpanAtEnd(lineObj);
4375 var mergedPos = merged && merged.find(0, true);
4376 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
4377 { lineN = lineNo(lineObj = mergedPos.to.line); }
4378 else
4379 { return found }
4380 }
4381 }
4382
4383 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4384 y -= widgetTopHeight(lineObj);
4385 var end = lineObj.text.length;
4386 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4387 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4388 return {begin: begin, end: end}
4389 }
4390
4391 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4392 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4393 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4394 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4395 }
4396
4397 // Returns true if the given side of a box is after the given
4398 // coordinates, in top-to-bottom, left-to-right order.
4399 function boxIsAfter(box, x, y, left) {
4400 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4401 }
4402
4403 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4404 // Move y into line-local coordinate space
4405 y -= heightAtLine(lineObj);
4406 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4407 // When directly calling `measureCharPrepared`, we have to adjust
4408 // for the widgets at this line.
4409 var widgetHeight$$1 = widgetTopHeight(lineObj);
4410 var begin = 0, end = lineObj.text.length, ltr = true;
4411
4412 var order = getOrder(lineObj, cm.doc.direction);
4413 // If the line isn't plain left-to-right text, first figure out
4414 // which bidi section the coordinates fall into.
4415 if (order) {
4416 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4417 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
4418 ltr = part.level != 1;
4419 // The awkward -1 offsets are needed because findFirst (called
4420 // on these below) will treat its first bound as inclusive,
4421 // second as exclusive, but we want to actually address the
4422 // characters in the part's range
4423 begin = ltr ? part.from : part.to - 1;
4424 end = ltr ? part.to : part.from - 1;
4425 }
4426
4427 // A binary search to find the first character whose bounding box
4428 // starts after the coordinates. If we run across any whose box wrap
4429 // the coordinates, store that.
4430 var chAround = null, boxAround = null;
4431 var ch = findFirst(function (ch) {
4432 var box = measureCharPrepared(cm, preparedMeasure, ch);
4433 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
4434 if (!boxIsAfter(box, x, y, false)) { return false }
4435 if (box.top <= y && box.left <= x) {
4436 chAround = ch;
4437 boxAround = box;
4438 }
4439 return true
4440 }, begin, end);
4441
4442 var baseX, sticky, outside = false;
4443 // If a box around the coordinates was found, use that
4444 if (boxAround) {
4445 // Distinguish coordinates nearer to the left or right side of the box
4446 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4447 ch = chAround + (atStart ? 0 : 1);
4448 sticky = atStart ? "after" : "before";
4449 baseX = atLeft ? boxAround.left : boxAround.right;
4450 } else {
4451 // (Adjust for extended bound, if necessary.)
4452 if (!ltr && (ch == end || ch == begin)) { ch++; }
4453 // To determine which side to associate with, get the box to the
4454 // left of the character and compare it's vertical position to the
4455 // coordinates
4456 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4457 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
4458 "after" : "before";
4459 // Now get accurate coordinates for this place, in order to get a
4460 // base X position
4461 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
4462 baseX = coords.left;
4463 outside = y < coords.top || y >= coords.bottom;
4464 }
4465
4466 ch = skipExtendingChars(lineObj.text, ch, 1);
4467 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
4468 }
4469
4470 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
4471 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4472 // situation, we can take this ordering to correspond to the visual
4473 // ordering. This finds the first part whose end is after the given
4474 // coordinates.
4475 var index = findFirst(function (i) {
4476 var part = order[i], ltr = part.level != 1;
4477 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
4478 "line", lineObj, preparedMeasure), x, y, true)
4479 }, 0, order.length - 1);
4480 var part = order[index];
4481 // If this isn't the first part, the part's start is also after
4482 // the coordinates, and the coordinates aren't on the same line as
4483 // that start, move one part back.
4484 if (index > 0) {
4485 var ltr = part.level != 1;
4486 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
4487 "line", lineObj, preparedMeasure);
4488 if (boxIsAfter(start, x, y, true) && start.top > y)
4489 { part = order[index - 1]; }
4490 }
4491 return part
4492 }
4493
4494 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4495 // In a wrapped line, rtl text on wrapping boundaries can do things
4496 // that don't correspond to the ordering in our `order` array at
4497 // all, so a binary search doesn't work, and we want to return a
4498 // part that only spans one line so that the binary search in
4499 // coordsCharInner is safe. As such, we first find the extent of the
4500 // wrapped line, and then do a flat search in which we discard any
4501 // spans that aren't on the line.
4502 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4503 var begin = ref.begin;
4504 var end = ref.end;
4505 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4506 var part = null, closestDist = null;
4507 for (var i = 0; i < order.length; i++) {
4508 var p = order[i];
4509 if (p.from >= end || p.to <= begin) { continue }
4510 var ltr = p.level != 1;
4511 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4512 // Weigh against spans ending before this, so that they are only
4513 // picked if nothing ends after
4514 var dist = endX < x ? x - endX + 1e9 : endX - x;
4515 if (!part || closestDist > dist) {
4516 part = p;
4517 closestDist = dist;
4518 }
4519 }
4520 if (!part) { part = order[order.length - 1]; }
4521 // Clip the part to the wrapped line.
4522 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4523 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4524 return part
4525 }
4526
4527 var measureText;
4528 // Compute the default text height.
4529 function textHeight(display) {
4530 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4531 if (measureText == null) {
4532 measureText = elt("pre");
4533 // Measure a bunch of lines, for browsers that compute
4534 // fractional heights.
4535 for (var i = 0; i < 49; ++i) {
4536 measureText.appendChild(document.createTextNode("x"));
4537 measureText.appendChild(elt("br"));
4538 }
4539 measureText.appendChild(document.createTextNode("x"));
4540 }
4541 removeChildrenAndAdd(display.measure, measureText);
4542 var height = measureText.offsetHeight / 50;
4543 if (height > 3) { display.cachedTextHeight = height; }
4544 removeChildren(display.measure);
4545 return height || 1
4546 }
4547
4548 // Compute the default character width.
4549 function charWidth(display) {
4550 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4551 var anchor = elt("span", "xxxxxxxxxx");
4552 var pre = elt("pre", [anchor]);
4553 removeChildrenAndAdd(display.measure, pre);
4554 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4555 if (width > 2) { display.cachedCharWidth = width; }
4556 return width || 10
4557 }
4558
4559 // Do a bulk-read of the DOM positions and sizes needed to draw the
4560 // view, so that we don't interleave reading and writing to the DOM.
4561 function getDimensions(cm) {
4562 var d = cm.display, left = {}, width = {};
4563 var gutterLeft = d.gutters.clientLeft;
4564 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4565 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
4566 width[cm.options.gutters[i]] = n.clientWidth;
4567 }
4568 return {fixedPos: compensateForHScroll(d),
4569 gutterTotalWidth: d.gutters.offsetWidth,
4570 gutterLeft: left,
4571 gutterWidth: width,
4572 wrapperWidth: d.wrapper.clientWidth}
4573 }
4574
4575 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4576 // but using getBoundingClientRect to get a sub-pixel-accurate
4577 // result.
4578 function compensateForHScroll(display) {
4579 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4580 }
4581
4582 // Returns a function that estimates the height of a line, to use as
4583 // first approximation until the line becomes visible (and is thus
4584 // properly measurable).
4585 function estimateHeight(cm) {
4586 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4587 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4588 return function (line) {
4589 if (lineIsHidden(cm.doc, line)) { return 0 }
4590
4591 var widgetsHeight = 0;
4592 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4593 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4594 } }
4595
4596 if (wrapping)
4597 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4598 else
4599 { return widgetsHeight + th }
4600 }
4601 }
4602
4603 function estimateLineHeights(cm) {
4604 var doc = cm.doc, est = estimateHeight(cm);
4605 doc.iter(function (line) {
4606 var estHeight = est(line);
4607 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4608 });
4609 }
4610
4611 // Given a mouse event, find the corresponding position. If liberal
4612 // is false, it checks whether a gutter or scrollbar was clicked,
4613 // and returns null if it was. forRect is used by rectangular
4614 // selections, and tries to estimate a character position even for
4615 // coordinates beyond the right of the text.
4616 function posFromMouse(cm, e, liberal, forRect) {
4617 var display = cm.display;
4618 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4619
4620 var x, y, space = display.lineSpace.getBoundingClientRect();
4621 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4622 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4623 catch (e) { return null }
4624 var coords = coordsChar(cm, x, y), line;
4625 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4626 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4627 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4628 }
4629 return coords
4630 }
4631
4632 // Find the view element corresponding to a given line. Return null
4633 // when the line isn't visible.
4634 function findViewIndex(cm, n) {
4635 if (n >= cm.display.viewTo) { return null }
4636 n -= cm.display.viewFrom;
4637 if (n < 0) { return null }
4638 var view = cm.display.view;
4639 for (var i = 0; i < view.length; i++) {
4640 n -= view[i].size;
4641 if (n < 0) { return i }
4642 }
4643 }
4644
4645 function updateSelection(cm) {
4646 cm.display.input.showSelection(cm.display.input.prepareSelection());
4647 }
4648
4649 function prepareSelection(cm, primary) {
4650 if ( primary === void 0 ) primary = true;
4651
4652 var doc = cm.doc, result = {};
4653 var curFragment = result.cursors = document.createDocumentFragment();
4654 var selFragment = result.selection = document.createDocumentFragment();
4655
4656 for (var i = 0; i < doc.sel.ranges.length; i++) {
4657 if (!primary && i == doc.sel.primIndex) { continue }
4658 var range$$1 = doc.sel.ranges[i];
4659 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4660 var collapsed = range$$1.empty();
4661 if (collapsed || cm.options.showCursorWhenSelecting)
4662 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4663 if (!collapsed)
4664 { drawSelectionRange(cm, range$$1, selFragment); }
4665 }
4666 return result
4667 }
4668
4669 // Draws a cursor for the given range
4670 function drawSelectionCursor(cm, head, output) {
4671 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4672
4673 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4674 cursor.style.left = pos.left + "px";
4675 cursor.style.top = pos.top + "px";
4676 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4677
4678 if (pos.other) {
4679 // Secondary cursor, shown when on a 'jump' in bi-directional text
4680 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4681 otherCursor.style.display = "";
4682 otherCursor.style.left = pos.other.left + "px";
4683 otherCursor.style.top = pos.other.top + "px";
4684 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4685 }
4686 }
4687
4688 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4689
4690 // Draws the given range as a highlighted selection
4691 function drawSelectionRange(cm, range$$1, output) {
4692 var display = cm.display, doc = cm.doc;
4693 var fragment = document.createDocumentFragment();
4694 var padding = paddingH(cm.display), leftSide = padding.left;
4695 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4696 var docLTR = doc.direction == "ltr";
4697
4698 function add(left, top, width, bottom) {
4699 if (top < 0) { top = 0; }
4700 top = Math.round(top);
4701 bottom = Math.round(bottom);
4702 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")));
4703 }
4704
4705 function drawForLine(line, fromArg, toArg) {
4706 var lineObj = getLine(doc, line);
4707 var lineLen = lineObj.text.length;
4708 var start, end;
4709 function coords(ch, bias) {
4710 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4711 }
4712
4713 function wrapX(pos, dir, side) {
4714 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4715 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4716 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4717 return coords(ch, prop)[prop]
4718 }
4719
4720 var order = getOrder(lineObj, doc.direction);
4721 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4722 var ltr = dir == "ltr";
4723 var fromPos = coords(from, ltr ? "left" : "right");
4724 var toPos = coords(to - 1, ltr ? "right" : "left");
4725
4726 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4727 var first = i == 0, last = !order || i == order.length - 1;
4728 if (toPos.top - fromPos.top <= 3) { // Single line
4729 var openLeft = (docLTR ? openStart : openEnd) && first;
4730 var openRight = (docLTR ? openEnd : openStart) && last;
4731 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4732 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4733 add(left, fromPos.top, right - left, fromPos.bottom);
4734 } else { // Multiple lines
4735 var topLeft, topRight, botLeft, botRight;
4736 if (ltr) {
4737 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4738 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4739 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4740 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4741 } else {
4742 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4743 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4744 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4745 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4746 }
4747 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4748 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4749 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4750 }
4751
4752 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4753 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4754 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4755 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4756 });
4757 return {start: start, end: end}
4758 }
4759
4760 var sFrom = range$$1.from(), sTo = range$$1.to();
4761 if (sFrom.line == sTo.line) {
4762 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4763 } else {
4764 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4765 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4766 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4767 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4768 if (singleVLine) {
4769 if (leftEnd.top < rightStart.top - 2) {
4770 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4771 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4772 } else {
4773 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4774 }
4775 }
4776 if (leftEnd.bottom < rightStart.top)
4777 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4778 }
4779
4780 output.appendChild(fragment);
4781 }
4782
4783 // Cursor-blinking
4784 function restartBlink(cm) {
4785 if (!cm.state.focused) { return }
4786 var display = cm.display;
4787 clearInterval(display.blinker);
4788 var on = true;
4789 display.cursorDiv.style.visibility = "";
4790 if (cm.options.cursorBlinkRate > 0)
4791 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4792 cm.options.cursorBlinkRate); }
4793 else if (cm.options.cursorBlinkRate < 0)
4794 { display.cursorDiv.style.visibility = "hidden"; }
4795 }
4796
4797 function ensureFocus(cm) {
4798 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4799 }
4800
4801 function delayBlurEvent(cm) {
4802 cm.state.delayingBlurEvent = true;
4803 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4804 cm.state.delayingBlurEvent = false;
4805 onBlur(cm);
4806 } }, 100);
4807 }
4808
4809 function onFocus(cm, e) {
4810 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4811
4812 if (cm.options.readOnly == "nocursor") { return }
4813 if (!cm.state.focused) {
4814 signal(cm, "focus", cm, e);
4815 cm.state.focused = true;
4816 addClass(cm.display.wrapper, "CodeMirror-focused");
4817 // This test prevents this from firing when a context
4818 // menu is closed (since the input reset would kill the
4819 // select-all detection hack)
4820 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4821 cm.display.input.reset();
4822 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4823 }
4824 cm.display.input.receivedFocus();
4825 }
4826 restartBlink(cm);
4827 }
4828 function onBlur(cm, e) {
4829 if (cm.state.delayingBlurEvent) { return }
4830
4831 if (cm.state.focused) {
4832 signal(cm, "blur", cm, e);
4833 cm.state.focused = false;
4834 rmClass(cm.display.wrapper, "CodeMirror-focused");
4835 }
4836 clearInterval(cm.display.blinker);
4837 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4838 }
4839
4840 // Read the actual heights of the rendered lines, and update their
4841 // stored heights to match.
4842 function updateHeightsInViewport(cm) {
4843 var display = cm.display;
4844 var prevBottom = display.lineDiv.offsetTop;
4845 for (var i = 0; i < display.view.length; i++) {
4846 var cur = display.view[i], height = (void 0);
4847 if (cur.hidden) { continue }
4848 if (ie && ie_version < 8) {
4849 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4850 height = bot - prevBottom;
4851 prevBottom = bot;
4852 } else {
4853 var box = cur.node.getBoundingClientRect();
4854 height = box.bottom - box.top;
4855 }
4856 var diff = cur.line.height - height;
4857 if (height < 2) { height = textHeight(display); }
4858 if (diff > .005 || diff < -.005) {
4859 updateLineHeight(cur.line, height);
4860 updateWidgetHeight(cur.line);
4861 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4862 { updateWidgetHeight(cur.rest[j]); } }
4863 }
4864 }
4865 }
4866
4867 // Read and store the height of line widgets associated with the
4868 // given line.
4869 function updateWidgetHeight(line) {
4870 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4871 var w = line.widgets[i], parent = w.node.parentNode;
4872 if (parent) { w.height = parent.offsetHeight; }
4873 } }
4874 }
4875
4876 // Compute the lines that are visible in a given viewport (defaults
4877 // the the current scroll position). viewport may contain top,
4878 // height, and ensure (see op.scrollToPos) properties.
4879 function visibleLines(display, doc, viewport) {
4880 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4881 top = Math.floor(top - paddingTop(display));
4882 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4883
4884 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4885 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4886 // forces those lines into the viewport (if possible).
4887 if (viewport && viewport.ensure) {
4888 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4889 if (ensureFrom < from) {
4890 from = ensureFrom;
4891 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4892 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4893 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4894 to = ensureTo;
4895 }
4896 }
4897 return {from: from, to: Math.max(to, from + 1)}
4898 }
4899
4900 // Re-align line numbers and gutter marks to compensate for
4901 // horizontal scrolling.
4902 function alignHorizontally(cm) {
4903 var display = cm.display, view = display.view;
4904 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4905 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4906 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4907 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4908 if (cm.options.fixedGutter) {
4909 if (view[i].gutter)
4910 { view[i].gutter.style.left = left; }
4911 if (view[i].gutterBackground)
4912 { view[i].gutterBackground.style.left = left; }
4913 }
4914 var align = view[i].alignable;
4915 if (align) { for (var j = 0; j < align.length; j++)
4916 { align[j].style.left = left; } }
4917 } }
4918 if (cm.options.fixedGutter)
4919 { display.gutters.style.left = (comp + gutterW) + "px"; }
4920 }
4921
4922 // Used to ensure that the line number gutter is still the right
4923 // size for the current document size. Returns true when an update
4924 // is needed.
4925 function maybeUpdateLineNumberWidth(cm) {
4926 if (!cm.options.lineNumbers) { return false }
4927 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4928 if (last.length != display.lineNumChars) {
4929 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4930 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4931 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4932 display.lineGutter.style.width = "";
4933 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4934 display.lineNumWidth = display.lineNumInnerWidth + padding;
4935 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4936 display.lineGutter.style.width = display.lineNumWidth + "px";
4937 updateGutterSpace(cm);
4938 return true
4939 }
4940 return false
4941 }
4942
4943 // SCROLLING THINGS INTO VIEW
4944
4945 // If an editor sits on the top or bottom of the window, partially
4946 // scrolled out of view, this ensures that the cursor is visible.
4947 function maybeScrollWindow(cm, rect) {
4948 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4949
4950 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4951 if (rect.top + box.top < 0) { doScroll = true; }
4952 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4953 if (doScroll != null && !phantom) {
4954 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;"));
4955 cm.display.lineSpace.appendChild(scrollNode);
4956 scrollNode.scrollIntoView(doScroll);
4957 cm.display.lineSpace.removeChild(scrollNode);
4958 }
4959 }
4960
4961 // Scroll a given position into view (immediately), verifying that
4962 // it actually became visible (as line heights are accurately
4963 // measured, the position of something may 'drift' during drawing).
4964 function scrollPosIntoView(cm, pos, end, margin) {
4965 if (margin == null) { margin = 0; }
4966 var rect;
4967 if (!cm.options.lineWrapping && pos == end) {
4968 // Set pos and end to the cursor positions around the character pos sticks to
4969 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4970 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4971 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
4972 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
4973 }
4974 for (var limit = 0; limit < 5; limit++) {
4975 var changed = false;
4976 var coords = cursorCoords(cm, pos);
4977 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4978 rect = {left: Math.min(coords.left, endCoords.left),
4979 top: Math.min(coords.top, endCoords.top) - margin,
4980 right: Math.max(coords.left, endCoords.left),
4981 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
4982 var scrollPos = calculateScrollPos(cm, rect);
4983 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4984 if (scrollPos.scrollTop != null) {
4985 updateScrollTop(cm, scrollPos.scrollTop);
4986 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
4987 }
4988 if (scrollPos.scrollLeft != null) {
4989 setScrollLeft(cm, scrollPos.scrollLeft);
4990 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
4991 }
4992 if (!changed) { break }
4993 }
4994 return rect
4995 }
4996
4997 // Scroll a given set of coordinates into view (immediately).
4998 function scrollIntoView(cm, rect) {
4999 var scrollPos = calculateScrollPos(cm, rect);
5000 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
5001 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
5002 }
5003
5004 // Calculate a new scroll position needed to scroll the given
5005 // rectangle into view. Returns an object with scrollTop and
5006 // scrollLeft properties. When these are undefined, the
5007 // vertical/horizontal position does not need to be adjusted.
5008 function calculateScrollPos(cm, rect) {
5009 var display = cm.display, snapMargin = textHeight(cm.display);
5010 if (rect.top < 0) { rect.top = 0; }
5011 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
5012 var screen = displayHeight(cm), result = {};
5013 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
5014 var docBottom = cm.doc.height + paddingVert(display);
5015 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
5016 if (rect.top < screentop) {
5017 result.scrollTop = atTop ? 0 : rect.top;
5018 } else if (rect.bottom > screentop + screen) {
5019 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
5020 if (newTop != screentop) { result.scrollTop = newTop; }
5021 }
5022
5023 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
5024 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
5025 var tooWide = rect.right - rect.left > screenw;
5026 if (tooWide) { rect.right = rect.left + screenw; }
5027 if (rect.left < 10)
5028 { result.scrollLeft = 0; }
5029 else if (rect.left < screenleft)
5030 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
5031 else if (rect.right > screenw + screenleft - 3)
5032 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
5033 return result
5034 }
5035
5036 // Store a relative adjustment to the scroll position in the current
5037 // operation (to be applied when the operation finishes).
5038 function addToScrollTop(cm, top) {
5039 if (top == null) { return }
5040 resolveScrollToPos(cm);
5041 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
5042 }
5043
5044 // Make sure that at the end of the operation the current cursor is
5045 // shown.
5046 function ensureCursorVisible(cm) {
5047 resolveScrollToPos(cm);
5048 var cur = cm.getCursor();
5049 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
5050 }
5051
5052 function scrollToCoords(cm, x, y) {
5053 if (x != null || y != null) { resolveScrollToPos(cm); }
5054 if (x != null) { cm.curOp.scrollLeft = x; }
5055 if (y != null) { cm.curOp.scrollTop = y; }
5056 }
5057
5058 function scrollToRange(cm, range$$1) {
5059 resolveScrollToPos(cm);
5060 cm.curOp.scrollToPos = range$$1;
5061 }
5062
5063 // When an operation has its scrollToPos property set, and another
5064 // scroll action is applied before the end of the operation, this
5065 // 'simulates' scrolling that position into view in a cheap way, so
5066 // that the effect of intermediate scroll commands is not ignored.
5067 function resolveScrollToPos(cm) {
5068 var range$$1 = cm.curOp.scrollToPos;
5069 if (range$$1) {
5070 cm.curOp.scrollToPos = null;
5071 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
5072 scrollToCoordsRange(cm, from, to, range$$1.margin);
5073 }
5074 }
5075
5076 function scrollToCoordsRange(cm, from, to, margin) {
5077 var sPos = calculateScrollPos(cm, {
5078 left: Math.min(from.left, to.left),
5079 top: Math.min(from.top, to.top) - margin,
5080 right: Math.max(from.right, to.right),
5081 bottom: Math.max(from.bottom, to.bottom) + margin
5082 });
5083 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
5084 }
5085
5086 // Sync the scrollable area and scrollbars, ensure the viewport
5087 // covers the visible area.
5088 function updateScrollTop(cm, val) {
5089 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
5090 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
5091 setScrollTop(cm, val, true);
5092 if (gecko) { updateDisplaySimple(cm); }
5093 startWorker(cm, 100);
5094 }
5095
5096 function setScrollTop(cm, val, forceScroll) {
5097 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
5098 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
5099 cm.doc.scrollTop = val;
5100 cm.display.scrollbars.setScrollTop(val);
5101 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
5102 }
5103
5104 // Sync scroller and scrollbar, ensure the gutter elements are
5105 // aligned.
5106 function setScrollLeft(cm, val, isScroller, forceScroll) {
5107 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
5108 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
5109 cm.doc.scrollLeft = val;
5110 alignHorizontally(cm);
5111 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
5112 cm.display.scrollbars.setScrollLeft(val);
5113 }
5114
5115 // SCROLLBARS
5116
5117 // Prepare DOM reads needed to update the scrollbars. Done in one
5118 // shot to minimize update/measure roundtrips.
5119 function measureForScrollbars(cm) {
5120 var d = cm.display, gutterW = d.gutters.offsetWidth;
5121 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
5122 return {
5123 clientHeight: d.scroller.clientHeight,
5124 viewHeight: d.wrapper.clientHeight,
5125 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5126 viewWidth: d.wrapper.clientWidth,
5127 barLeft: cm.options.fixedGutter ? gutterW : 0,
5128 docHeight: docH,
5129 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5130 nativeBarWidth: d.nativeBarWidth,
5131 gutterWidth: gutterW
5132 }
5133 }
5134
5135 var NativeScrollbars = function(place, scroll, cm) {
5136 this.cm = cm;
5137 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5138 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5139 place(vert); place(horiz);
5140
5141 on(vert, "scroll", function () {
5142 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5143 });
5144 on(horiz, "scroll", function () {
5145 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5146 });
5147
5148 this.checkedZeroWidth = false;
5149 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5150 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5151 };
5152
5153 NativeScrollbars.prototype.update = function (measure) {
5154 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5155 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5156 var sWidth = measure.nativeBarWidth;
5157
5158 if (needsV) {
5159 this.vert.style.display = "block";
5160 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5161 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5162 // A bug in IE8 can cause this value to be negative, so guard it.
5163 this.vert.firstChild.style.height =
5164 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5165 } else {
5166 this.vert.style.display = "";
5167 this.vert.firstChild.style.height = "0";
5168 }
5169
5170 if (needsH) {
5171 this.horiz.style.display = "block";
5172 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5173 this.horiz.style.left = measure.barLeft + "px";
5174 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5175 this.horiz.firstChild.style.width =
5176 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5177 } else {
5178 this.horiz.style.display = "";
5179 this.horiz.firstChild.style.width = "0";
5180 }
5181
5182 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5183 if (sWidth == 0) { this.zeroWidthHack(); }
5184 this.checkedZeroWidth = true;
5185 }
5186
5187 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5188 };
5189
5190 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5191 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5192 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5193 };
5194
5195 NativeScrollbars.prototype.setScrollTop = function (pos) {
5196 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5197 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5198 };
5199
5200 NativeScrollbars.prototype.zeroWidthHack = function () {
5201 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5202 this.horiz.style.height = this.vert.style.width = w;
5203 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5204 this.disableHoriz = new Delayed;
5205 this.disableVert = new Delayed;
5206 };
5207
5208 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5209 bar.style.pointerEvents = "auto";
5210 function maybeDisable() {
5211 // To find out whether the scrollbar is still visible, we
5212 // check whether the element under the pixel in the bottom
5213 // right corner of the scrollbar box is the scrollbar box
5214 // itself (when the bar is still visible) or its filler child
5215 // (when the bar is hidden). If it is still visible, we keep
5216 // it enabled, if it's hidden, we disable pointer events.
5217 var box = bar.getBoundingClientRect();
5218 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5219 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5220 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5221 else { delay.set(1000, maybeDisable); }
5222 }
5223 delay.set(1000, maybeDisable);
5224 };
5225
5226 NativeScrollbars.prototype.clear = function () {
5227 var parent = this.horiz.parentNode;
5228 parent.removeChild(this.horiz);
5229 parent.removeChild(this.vert);
5230 };
5231
5232 var NullScrollbars = function () {};
5233
5234 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5235 NullScrollbars.prototype.setScrollLeft = function () {};
5236 NullScrollbars.prototype.setScrollTop = function () {};
5237 NullScrollbars.prototype.clear = function () {};
5238
5239 function updateScrollbars(cm, measure) {
5240 if (!measure) { measure = measureForScrollbars(cm); }
5241 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5242 updateScrollbarsInner(cm, measure);
5243 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5244 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5245 { updateHeightsInViewport(cm); }
5246 updateScrollbarsInner(cm, measureForScrollbars(cm));
5247 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5248 }
5249 }
5250
5251 // Re-synchronize the fake scrollbars with the actual size of the
5252 // content.
5253 function updateScrollbarsInner(cm, measure) {
5254 var d = cm.display;
5255 var sizes = d.scrollbars.update(measure);
5256
5257 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5258 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5259 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5260
5261 if (sizes.right && sizes.bottom) {
5262 d.scrollbarFiller.style.display = "block";
5263 d.scrollbarFiller.style.height = sizes.bottom + "px";
5264 d.scrollbarFiller.style.width = sizes.right + "px";
5265 } else { d.scrollbarFiller.style.display = ""; }
5266 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5267 d.gutterFiller.style.display = "block";
5268 d.gutterFiller.style.height = sizes.bottom + "px";
5269 d.gutterFiller.style.width = measure.gutterWidth + "px";
5270 } else { d.gutterFiller.style.display = ""; }
5271 }
5272
5273 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5274
5275 function initScrollbars(cm) {
5276 if (cm.display.scrollbars) {
5277 cm.display.scrollbars.clear();
5278 if (cm.display.scrollbars.addClass)
5279 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5280 }
5281
5282 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5283 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5284 // Prevent clicks in the scrollbars from killing focus
5285 on(node, "mousedown", function () {
5286 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5287 });
5288 node.setAttribute("cm-not-content", "true");
5289 }, function (pos, axis) {
5290 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5291 else { updateScrollTop(cm, pos); }
5292 }, cm);
5293 if (cm.display.scrollbars.addClass)
5294 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5295 }
5296
5297 // Operations are used to wrap a series of changes to the editor
5298 // state in such a way that each change won't have to update the
5299 // cursor and display (which would be awkward, slow, and
5300 // error-prone). Instead, display updates are batched and then all
5301 // combined and executed at once.
5302
5303 var nextOpId = 0;
5304 // Start a new operation.
5305 function startOperation(cm) {
5306 cm.curOp = {
5307 cm: cm,
5308 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5309 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5310 forceUpdate: false, // Used to force a redraw
5311 updateInput: null, // Whether to reset the input textarea
5312 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5313 changeObjs: null, // Accumulated changes, for firing change events
5314 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5315 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5316 selectionChanged: false, // Whether the selection needs to be redrawn
5317 updateMaxLine: false, // Set when the widest line needs to be determined anew
5318 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5319 scrollToPos: null, // Used to scroll to a specific position
5320 focus: false,
5321 id: ++nextOpId // Unique ID
5322 };
5323 pushOperation(cm.curOp);
5324 }
5325
5326 // Finish an operation, updating the display and signalling delayed events
5327 function endOperation(cm) {
5328 var op = cm.curOp;
5329 finishOperation(op, function (group) {
5330 for (var i = 0; i < group.ops.length; i++)
5331 { group.ops[i].cm.curOp = null; }
5332 endOperations(group);
5333 });
5334 }
5335
5336 // The DOM updates done when an operation finishes are batched so
5337 // that the minimum number of relayouts are required.
5338 function endOperations(group) {
5339 var ops = group.ops;
5340 for (var i = 0; i < ops.length; i++) // Read DOM
5341 { endOperation_R1(ops[i]); }
5342 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5343 { endOperation_W1(ops[i$1]); }
5344 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5345 { endOperation_R2(ops[i$2]); }
5346 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5347 { endOperation_W2(ops[i$3]); }
5348 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5349 { endOperation_finish(ops[i$4]); }
5350 }
5351
5352 function endOperation_R1(op) {
5353 var cm = op.cm, display = cm.display;
5354 maybeClipScrollbars(cm);
5355 if (op.updateMaxLine) { findMaxLine(cm); }
5356
5357 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5358 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5359 op.scrollToPos.to.line >= display.viewTo) ||
5360 display.maxLineChanged && cm.options.lineWrapping;
5361 op.update = op.mustUpdate &&
5362 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5363 }
5364
5365 function endOperation_W1(op) {
5366 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5367 }
5368
5369 function endOperation_R2(op) {
5370 var cm = op.cm, display = cm.display;
5371 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5372
5373 op.barMeasure = measureForScrollbars(cm);
5374
5375 // If the max line changed since it was last measured, measure it,
5376 // and ensure the document's width matches it.
5377 // updateDisplay_W2 will use these properties to do the actual resizing
5378 if (display.maxLineChanged && !cm.options.lineWrapping) {
5379 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5380 cm.display.sizerWidth = op.adjustWidthTo;
5381 op.barMeasure.scrollWidth =
5382 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5383 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5384 }
5385
5386 if (op.updatedDisplay || op.selectionChanged)
5387 { op.preparedSelection = display.input.prepareSelection(); }
5388 }
5389
5390 function endOperation_W2(op) {
5391 var cm = op.cm;
5392
5393 if (op.adjustWidthTo != null) {
5394 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5395 if (op.maxScrollLeft < cm.doc.scrollLeft)
5396 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5397 cm.display.maxLineChanged = false;
5398 }
5399
5400 var takeFocus = op.focus && op.focus == activeElt();
5401 if (op.preparedSelection)
5402 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5403 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5404 { updateScrollbars(cm, op.barMeasure); }
5405 if (op.updatedDisplay)
5406 { setDocumentHeight(cm, op.barMeasure); }
5407
5408 if (op.selectionChanged) { restartBlink(cm); }
5409
5410 if (cm.state.focused && op.updateInput)
5411 { cm.display.input.reset(op.typing); }
5412 if (takeFocus) { ensureFocus(op.cm); }
5413 }
5414
5415 function endOperation_finish(op) {
5416 var cm = op.cm, display = cm.display, doc = cm.doc;
5417
5418 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5419
5420 // Abort mouse wheel delta measurement, when scrolling explicitly
5421 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5422 { display.wheelStartX = display.wheelStartY = null; }
5423
5424 // Propagate the scroll position to the actual DOM scroller
5425 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5426
5427 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5428 // If we need to scroll a specific position into view, do so.
5429 if (op.scrollToPos) {
5430 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5431 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5432 maybeScrollWindow(cm, rect);
5433 }
5434
5435 // Fire events for markers that are hidden/unidden by editing or
5436 // undoing
5437 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5438 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5439 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5440 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5441 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5442
5443 if (display.wrapper.offsetHeight)
5444 { doc.scrollTop = cm.display.scroller.scrollTop; }
5445
5446 // Fire change events, and delayed event handlers
5447 if (op.changeObjs)
5448 { signal(cm, "changes", cm, op.changeObjs); }
5449 if (op.update)
5450 { op.update.finish(); }
5451 }
5452
5453 // Run the given function in an operation
5454 function runInOp(cm, f) {
5455 if (cm.curOp) { return f() }
5456 startOperation(cm);
5457 try { return f() }
5458 finally { endOperation(cm); }
5459 }
5460 // Wraps a function in an operation. Returns the wrapped function.
5461 function operation(cm, f) {
5462 return function() {
5463 if (cm.curOp) { return f.apply(cm, arguments) }
5464 startOperation(cm);
5465 try { return f.apply(cm, arguments) }
5466 finally { endOperation(cm); }
5467 }
5468 }
5469 // Used to add methods to editor and doc instances, wrapping them in
5470 // operations.
5471 function methodOp(f) {
5472 return function() {
5473 if (this.curOp) { return f.apply(this, arguments) }
5474 startOperation(this);
5475 try { return f.apply(this, arguments) }
5476 finally { endOperation(this); }
5477 }
5478 }
5479 function docMethodOp(f) {
5480 return function() {
5481 var cm = this.cm;
5482 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5483 startOperation(cm);
5484 try { return f.apply(this, arguments) }
5485 finally { endOperation(cm); }
5486 }
5487 }
5488
5489 // Updates the display.view data structure for a given change to the
5490 // document. From and to are in pre-change coordinates. Lendiff is
5491 // the amount of lines added or subtracted by the change. This is
5492 // used for changes that span multiple lines, or change the way
5493 // lines are divided into visual lines. regLineChange (below)
5494 // registers single-line changes.
5495 function regChange(cm, from, to, lendiff) {
5496 if (from == null) { from = cm.doc.first; }
5497 if (to == null) { to = cm.doc.first + cm.doc.size; }
5498 if (!lendiff) { lendiff = 0; }
5499
5500 var display = cm.display;
5501 if (lendiff && to < display.viewTo &&
5502 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5503 { display.updateLineNumbers = from; }
5504
5505 cm.curOp.viewChanged = true;
5506
5507 if (from >= display.viewTo) { // Change after
5508 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5509 { resetView(cm); }
5510 } else if (to <= display.viewFrom) { // Change before
5511 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5512 resetView(cm);
5513 } else {
5514 display.viewFrom += lendiff;
5515 display.viewTo += lendiff;
5516 }
5517 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5518 resetView(cm);
5519 } else if (from <= display.viewFrom) { // Top overlap
5520 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5521 if (cut) {
5522 display.view = display.view.slice(cut.index);
5523 display.viewFrom = cut.lineN;
5524 display.viewTo += lendiff;
5525 } else {
5526 resetView(cm);
5527 }
5528 } else if (to >= display.viewTo) { // Bottom overlap
5529 var cut$1 = viewCuttingPoint(cm, from, from, -1);
5530 if (cut$1) {
5531 display.view = display.view.slice(0, cut$1.index);
5532 display.viewTo = cut$1.lineN;
5533 } else {
5534 resetView(cm);
5535 }
5536 } else { // Gap in the middle
5537 var cutTop = viewCuttingPoint(cm, from, from, -1);
5538 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5539 if (cutTop && cutBot) {
5540 display.view = display.view.slice(0, cutTop.index)
5541 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5542 .concat(display.view.slice(cutBot.index));
5543 display.viewTo += lendiff;
5544 } else {
5545 resetView(cm);
5546 }
5547 }
5548
5549 var ext = display.externalMeasured;
5550 if (ext) {
5551 if (to < ext.lineN)
5552 { ext.lineN += lendiff; }
5553 else if (from < ext.lineN + ext.size)
5554 { display.externalMeasured = null; }
5555 }
5556 }
5557
5558 // Register a change to a single line. Type must be one of "text",
5559 // "gutter", "class", "widget"
5560 function regLineChange(cm, line, type) {
5561 cm.curOp.viewChanged = true;
5562 var display = cm.display, ext = cm.display.externalMeasured;
5563 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5564 { display.externalMeasured = null; }
5565
5566 if (line < display.viewFrom || line >= display.viewTo) { return }
5567 var lineView = display.view[findViewIndex(cm, line)];
5568 if (lineView.node == null) { return }
5569 var arr = lineView.changes || (lineView.changes = []);
5570 if (indexOf(arr, type) == -1) { arr.push(type); }
5571 }
5572
5573 // Clear the view.
5574 function resetView(cm) {
5575 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5576 cm.display.view = [];
5577 cm.display.viewOffset = 0;
5578 }
5579
5580 function viewCuttingPoint(cm, oldN, newN, dir) {
5581 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5582 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5583 { return {index: index, lineN: newN} }
5584 var n = cm.display.viewFrom;
5585 for (var i = 0; i < index; i++)
5586 { n += view[i].size; }
5587 if (n != oldN) {
5588 if (dir > 0) {
5589 if (index == view.length - 1) { return null }
5590 diff = (n + view[index].size) - oldN;
5591 index++;
5592 } else {
5593 diff = n - oldN;
5594 }
5595 oldN += diff; newN += diff;
5596 }
5597 while (visualLineNo(cm.doc, newN) != newN) {
5598 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
5599 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5600 index += dir;
5601 }
5602 return {index: index, lineN: newN}
5603 }
5604
5605 // Force the view to cover a given range, adding empty view element
5606 // or clipping off existing ones as needed.
5607 function adjustView(cm, from, to) {
5608 var display = cm.display, view = display.view;
5609 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5610 display.view = buildViewArray(cm, from, to);
5611 display.viewFrom = from;
5612 } else {
5613 if (display.viewFrom > from)
5614 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
5615 else if (display.viewFrom < from)
5616 { display.view = display.view.slice(findViewIndex(cm, from)); }
5617 display.viewFrom = from;
5618 if (display.viewTo < to)
5619 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
5620 else if (display.viewTo > to)
5621 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
5622 }
5623 display.viewTo = to;
5624 }
5625
5626 // Count the number of lines in the view whose DOM representation is
5627 // out of date (or nonexistent).
5628 function countDirtyView(cm) {
5629 var view = cm.display.view, dirty = 0;
5630 for (var i = 0; i < view.length; i++) {
5631 var lineView = view[i];
5632 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
5633 }
5634 return dirty
5635 }
5636
5637 // HIGHLIGHT WORKER
5638
5639 function startWorker(cm, time) {
5640 if (cm.doc.highlightFrontier < cm.display.viewTo)
5641 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5642 }
5643
5644 function highlightWorker(cm) {
5645 var doc = cm.doc;
5646 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5647 var end = +new Date + cm.options.workTime;
5648 var context = getContextBefore(cm, doc.highlightFrontier);
5649 var changedLines = [];
5650
5651 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5652 if (context.line >= cm.display.viewFrom) { // Visible
5653 var oldStyles = line.styles;
5654 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5655 var highlighted = highlightLine(cm, line, context, true);
5656 if (resetState) { context.state = resetState; }
5657 line.styles = highlighted.styles;
5658 var oldCls = line.styleClasses, newCls = highlighted.classes;
5659 if (newCls) { line.styleClasses = newCls; }
5660 else if (oldCls) { line.styleClasses = null; }
5661 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5662 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5663 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5664 if (ischange) { changedLines.push(context.line); }
5665 line.stateAfter = context.save();
5666 context.nextLine();
5667 } else {
5668 if (line.text.length <= cm.options.maxHighlightLength)
5669 { processLine(cm, line.text, context); }
5670 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5671 context.nextLine();
5672 }
5673 if (+new Date > end) {
5674 startWorker(cm, cm.options.workDelay);
5675 return true
5676 }
5677 });
5678 doc.highlightFrontier = context.line;
5679 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5680 if (changedLines.length) { runInOp(cm, function () {
5681 for (var i = 0; i < changedLines.length; i++)
5682 { regLineChange(cm, changedLines[i], "text"); }
5683 }); }
5684 }
5685
5686 // DISPLAY DRAWING
5687
5688 var DisplayUpdate = function(cm, viewport, force) {
5689 var display = cm.display;
5690
5691 this.viewport = viewport;
5692 // Store some values that we'll need later (but don't want to force a relayout for)
5693 this.visible = visibleLines(display, cm.doc, viewport);
5694 this.editorIsHidden = !display.wrapper.offsetWidth;
5695 this.wrapperHeight = display.wrapper.clientHeight;
5696 this.wrapperWidth = display.wrapper.clientWidth;
5697 this.oldDisplayWidth = displayWidth(cm);
5698 this.force = force;
5699 this.dims = getDimensions(cm);
5700 this.events = [];
5701 };
5702
5703 DisplayUpdate.prototype.signal = function (emitter, type) {
5704 if (hasHandler(emitter, type))
5705 { this.events.push(arguments); }
5706 };
5707 DisplayUpdate.prototype.finish = function () {
5708 var this$1 = this;
5709
5710 for (var i = 0; i < this.events.length; i++)
5711 { signal.apply(null, this$1.events[i]); }
5712 };
5713
5714 function maybeClipScrollbars(cm) {
5715 var display = cm.display;
5716 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5717 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5718 display.heightForcer.style.height = scrollGap(cm) + "px";
5719 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5720 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5721 display.scrollbarsClipped = true;
5722 }
5723 }
5724
5725 function selectionSnapshot(cm) {
5726 if (cm.hasFocus()) { return null }
5727 var active = activeElt();
5728 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5729 var result = {activeElt: active};
5730 if (window.getSelection) {
5731 var sel = window.getSelection();
5732 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5733 result.anchorNode = sel.anchorNode;
5734 result.anchorOffset = sel.anchorOffset;
5735 result.focusNode = sel.focusNode;
5736 result.focusOffset = sel.focusOffset;
5737 }
5738 }
5739 return result
5740 }
5741
5742 function restoreSelection(snapshot) {
5743 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5744 snapshot.activeElt.focus();
5745 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5746 var sel = window.getSelection(), range$$1 = document.createRange();
5747 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5748 range$$1.collapse(false);
5749 sel.removeAllRanges();
5750 sel.addRange(range$$1);
5751 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5752 }
5753 }
5754
5755 // Does the actual updating of the line display. Bails out
5756 // (returning false) when there is nothing to be done and forced is
5757 // false.
5758 function updateDisplayIfNeeded(cm, update) {
5759 var display = cm.display, doc = cm.doc;
5760
5761 if (update.editorIsHidden) {
5762 resetView(cm);
5763 return false
5764 }
5765
5766 // Bail out if the visible area is already rendered and nothing changed.
5767 if (!update.force &&
5768 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5769 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5770 display.renderedView == display.view && countDirtyView(cm) == 0)
5771 { return false }
5772
5773 if (maybeUpdateLineNumberWidth(cm)) {
5774 resetView(cm);
5775 update.dims = getDimensions(cm);
5776 }
5777
5778 // Compute a suitable new viewport (from & to)
5779 var end = doc.first + doc.size;
5780 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5781 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5782 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5783 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5784 if (sawCollapsedSpans) {
5785 from = visualLineNo(cm.doc, from);
5786 to = visualLineEndNo(cm.doc, to);
5787 }
5788
5789 var different = from != display.viewFrom || to != display.viewTo ||
5790 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5791 adjustView(cm, from, to);
5792
5793 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5794 // Position the mover div to align with the current scroll position
5795 cm.display.mover.style.top = display.viewOffset + "px";
5796
5797 var toUpdate = countDirtyView(cm);
5798 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5799 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5800 { return false }
5801
5802 // For big changes, we hide the enclosing element during the
5803 // update, since that speeds up the operations on most browsers.
5804 var selSnapshot = selectionSnapshot(cm);
5805 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5806 patchDisplay(cm, display.updateLineNumbers, update.dims);
5807 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5808 display.renderedView = display.view;
5809 // There might have been a widget with a focused element that got
5810 // hidden or updated, if so re-focus it.
5811 restoreSelection(selSnapshot);
5812
5813 // Prevent selection and cursors from interfering with the scroll
5814 // width and height.
5815 removeChildren(display.cursorDiv);
5816 removeChildren(display.selectionDiv);
5817 display.gutters.style.height = display.sizer.style.minHeight = 0;
5818
5819 if (different) {
5820 display.lastWrapHeight = update.wrapperHeight;
5821 display.lastWrapWidth = update.wrapperWidth;
5822 startWorker(cm, 400);
5823 }
5824
5825 display.updateLineNumbers = null;
5826
5827 return true
5828 }
5829
5830 function postUpdateDisplay(cm, update) {
5831 var viewport = update.viewport;
5832
5833 for (var first = true;; first = false) {
5834 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5835 // Clip forced viewport to actual scrollable area.
5836 if (viewport && viewport.top != null)
5837 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5838 // Updated line heights might result in the drawn area not
5839 // actually covering the viewport. Keep looping until it does.
5840 update.visible = visibleLines(cm.display, cm.doc, viewport);
5841 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5842 { break }
5843 }
5844 if (!updateDisplayIfNeeded(cm, update)) { break }
5845 updateHeightsInViewport(cm);
5846 var barMeasure = measureForScrollbars(cm);
5847 updateSelection(cm);
5848 updateScrollbars(cm, barMeasure);
5849 setDocumentHeight(cm, barMeasure);
5850 update.force = false;
5851 }
5852
5853 update.signal(cm, "update", cm);
5854 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5855 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5856 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5857 }
5858 }
5859
5860 function updateDisplaySimple(cm, viewport) {
5861 var update = new DisplayUpdate(cm, viewport);
5862 if (updateDisplayIfNeeded(cm, update)) {
5863 updateHeightsInViewport(cm);
5864 postUpdateDisplay(cm, update);
5865 var barMeasure = measureForScrollbars(cm);
5866 updateSelection(cm);
5867 updateScrollbars(cm, barMeasure);
5868 setDocumentHeight(cm, barMeasure);
5869 update.finish();
5870 }
5871 }
5872
5873 // Sync the actual display DOM structure with display.view, removing
5874 // nodes for lines that are no longer in view, and creating the ones
5875 // that are not there yet, and updating the ones that are out of
5876 // date.
5877 function patchDisplay(cm, updateNumbersFrom, dims) {
5878 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5879 var container = display.lineDiv, cur = container.firstChild;
5880
5881 function rm(node) {
5882 var next = node.nextSibling;
5883 // Works around a throw-scroll bug in OS X Webkit
5884 if (webkit && mac && cm.display.currentWheelTarget == node)
5885 { node.style.display = "none"; }
5886 else
5887 { node.parentNode.removeChild(node); }
5888 return next
5889 }
5890
5891 var view = display.view, lineN = display.viewFrom;
5892 // Loop over the elements in the view, syncing cur (the DOM nodes
5893 // in display.lineDiv) with the view as we go.
5894 for (var i = 0; i < view.length; i++) {
5895 var lineView = view[i];
5896 if (lineView.hidden) {
5897 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5898 var node = buildLineElement(cm, lineView, lineN, dims);
5899 container.insertBefore(node, cur);
5900 } else { // Already drawn
5901 while (cur != lineView.node) { cur = rm(cur); }
5902 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5903 updateNumbersFrom <= lineN && lineView.lineNumber;
5904 if (lineView.changes) {
5905 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5906 updateLineForChanges(cm, lineView, lineN, dims);
5907 }
5908 if (updateNumber) {
5909 removeChildren(lineView.lineNumber);
5910 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5911 }
5912 cur = lineView.node.nextSibling;
5913 }
5914 lineN += lineView.size;
5915 }
5916 while (cur) { cur = rm(cur); }
5917 }
5918
5919 function updateGutterSpace(cm) {
5920 var width = cm.display.gutters.offsetWidth;
5921 cm.display.sizer.style.marginLeft = width + "px";
5922 }
5923
5924 function setDocumentHeight(cm, measure) {
5925 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5926 cm.display.heightForcer.style.top = measure.docHeight + "px";
5927 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5928 }
5929
5930 // Rebuild the gutter elements, ensure the margin to the left of the
5931 // code matches their width.
5932 function updateGutters(cm) {
5933 var gutters = cm.display.gutters, specs = cm.options.gutters;
5934 removeChildren(gutters);
5935 var i = 0;
5936 for (; i < specs.length; ++i) {
5937 var gutterClass = specs[i];
5938 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
5939 if (gutterClass == "CodeMirror-linenumbers") {
5940 cm.display.lineGutter = gElt;
5941 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
5942 }
5943 }
5944 gutters.style.display = i ? "" : "none";
5945 updateGutterSpace(cm);
5946 }
5947
5948 // Make sure the gutters options contains the element
5949 // "CodeMirror-linenumbers" when the lineNumbers option is true.
5950 function setGuttersForLineNumbers(options) {
5951 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
5952 if (found == -1 && options.lineNumbers) {
5953 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
5954 } else if (found > -1 && !options.lineNumbers) {
5955 options.gutters = options.gutters.slice(0);
5956 options.gutters.splice(found, 1);
5957 }
5958 }
5959
5960 // Since the delta values reported on mouse wheel events are
5961 // unstandardized between browsers and even browser versions, and
5962 // generally horribly unpredictable, this code starts by measuring
5963 // the scroll effect that the first few mouse wheel events have,
5964 // and, from that, detects the way it can convert deltas to pixel
5965 // offsets afterwards.
5966 //
5967 // The reason we want to know the amount a wheel event will scroll
5968 // is that it gives us a chance to update the display before the
5969 // actual scrolling happens, reducing flickering.
5970
5971 var wheelSamples = 0;
5972 var wheelPixelsPerUnit = null;
5973 // Fill in a browser-detected starting value on browsers where we
5974 // know one. These don't have to be accurate -- the result of them
5975 // being wrong would just be a slight flicker on the first wheel
5976 // scroll (if it is large enough).
5977 if (ie) { wheelPixelsPerUnit = -.53; }
5978 else if (gecko) { wheelPixelsPerUnit = 15; }
5979 else if (chrome) { wheelPixelsPerUnit = -.7; }
5980 else if (safari) { wheelPixelsPerUnit = -1/3; }
5981
5982 function wheelEventDelta(e) {
5983 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
5984 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
5985 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
5986 else if (dy == null) { dy = e.wheelDelta; }
5987 return {x: dx, y: dy}
5988 }
5989 function wheelEventPixels(e) {
5990 var delta = wheelEventDelta(e);
5991 delta.x *= wheelPixelsPerUnit;
5992 delta.y *= wheelPixelsPerUnit;
5993 return delta
5994 }
5995
5996 function onScrollWheel(cm, e) {
5997 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
5998
5999 var display = cm.display, scroll = display.scroller;
6000 // Quit if there's nothing to scroll here
6001 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
6002 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
6003 if (!(dx && canScrollX || dy && canScrollY)) { return }
6004
6005 // Webkit browsers on OS X abort momentum scrolls when the target
6006 // of the scroll event is removed from the scrollable element.
6007 // This hack (see related code in patchDisplay) makes sure the
6008 // element is kept around.
6009 if (dy && mac && webkit) {
6010 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
6011 for (var i = 0; i < view.length; i++) {
6012 if (view[i].node == cur) {
6013 cm.display.currentWheelTarget = cur;
6014 break outer
6015 }
6016 }
6017 }
6018 }
6019
6020 // On some browsers, horizontal scrolling will cause redraws to
6021 // happen before the gutter has been realigned, causing it to
6022 // wriggle around in a most unseemly way. When we have an
6023 // estimated pixels/delta value, we just handle horizontal
6024 // scrolling entirely here. It'll be slightly off from native, but
6025 // better than glitching out.
6026 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6027 if (dy && canScrollY)
6028 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
6029 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
6030 // Only prevent default scrolling if vertical scrolling is
6031 // actually possible. Otherwise, it causes vertical scroll
6032 // jitter on OSX trackpads when deltaX is small and deltaY
6033 // is large (issue #3579)
6034 if (!dy || (dy && canScrollY))
6035 { e_preventDefault(e); }
6036 display.wheelStartX = null; // Abort measurement, if in progress
6037 return
6038 }
6039
6040 // 'Project' the visible viewport to cover the area that is being
6041 // scrolled into view (if we know enough to estimate it).
6042 if (dy && wheelPixelsPerUnit != null) {
6043 var pixels = dy * wheelPixelsPerUnit;
6044 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6045 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
6046 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
6047 updateDisplaySimple(cm, {top: top, bottom: bot});
6048 }
6049
6050 if (wheelSamples < 20) {
6051 if (display.wheelStartX == null) {
6052 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6053 display.wheelDX = dx; display.wheelDY = dy;
6054 setTimeout(function () {
6055 if (display.wheelStartX == null) { return }
6056 var movedX = scroll.scrollLeft - display.wheelStartX;
6057 var movedY = scroll.scrollTop - display.wheelStartY;
6058 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6059 (movedX && display.wheelDX && movedX / display.wheelDX);
6060 display.wheelStartX = display.wheelStartY = null;
6061 if (!sample) { return }
6062 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6063 ++wheelSamples;
6064 }, 200);
6065 } else {
6066 display.wheelDX += dx; display.wheelDY += dy;
6067 }
6068 }
6069 }
6070
6071 // Selection objects are immutable. A new one is created every time
6072 // the selection changes. A selection is one or more non-overlapping
6073 // (and non-touching) ranges, sorted, and an integer that indicates
6074 // which one is the primary selection (the one that's scrolled into
6075 // view, that getCursor returns, etc).
6076 var Selection = function(ranges, primIndex) {
6077 this.ranges = ranges;
6078 this.primIndex = primIndex;
6079 };
6080
6081 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
6082
6083 Selection.prototype.equals = function (other) {
6084 var this$1 = this;
6085
6086 if (other == this) { return true }
6087 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
6088 for (var i = 0; i < this.ranges.length; i++) {
6089 var here = this$1.ranges[i], there = other.ranges[i];
6090 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
6091 }
6092 return true
6093 };
6094
6095 Selection.prototype.deepCopy = function () {
6096 var this$1 = this;
6097
6098 var out = [];
6099 for (var i = 0; i < this.ranges.length; i++)
6100 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
6101 return new Selection(out, this.primIndex)
6102 };
6103
6104 Selection.prototype.somethingSelected = function () {
6105 var this$1 = this;
6106
6107 for (var i = 0; i < this.ranges.length; i++)
6108 { if (!this$1.ranges[i].empty()) { return true } }
6109 return false
6110 };
6111
6112 Selection.prototype.contains = function (pos, end) {
6113 var this$1 = this;
6114
6115 if (!end) { end = pos; }
6116 for (var i = 0; i < this.ranges.length; i++) {
6117 var range = this$1.ranges[i];
6118 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
6119 { return i }
6120 }
6121 return -1
6122 };
6123
6124 var Range = function(anchor, head) {
6125 this.anchor = anchor; this.head = head;
6126 };
6127
6128 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6129 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6130 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6131
6132 // Take an unsorted, potentially overlapping set of ranges, and
6133 // build a selection out of it. 'Consumes' ranges array (modifying
6134 // it).
6135 function normalizeSelection(ranges, primIndex) {
6136 var prim = ranges[primIndex];
6137 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6138 primIndex = indexOf(ranges, prim);
6139 for (var i = 1; i < ranges.length; i++) {
6140 var cur = ranges[i], prev = ranges[i - 1];
6141 if (cmp(prev.to(), cur.from()) >= 0) {
6142 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6143 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6144 if (i <= primIndex) { --primIndex; }
6145 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6146 }
6147 }
6148 return new Selection(ranges, primIndex)
6149 }
6150
6151 function simpleSelection(anchor, head) {
6152 return new Selection([new Range(anchor, head || anchor)], 0)
6153 }
6154
6155 // Compute the position of the end of a change (its 'to' property
6156 // refers to the pre-change end).
6157 function changeEnd(change) {
6158 if (!change.text) { return change.to }
6159 return Pos(change.from.line + change.text.length - 1,
6160 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6161 }
6162
6163 // Adjust a position to refer to the post-change position of the
6164 // same text, or the end of the change if the change covers it.
6165 function adjustForChange(pos, change) {
6166 if (cmp(pos, change.from) < 0) { return pos }
6167 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6168
6169 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6170 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6171 return Pos(line, ch)
6172 }
6173
6174 function computeSelAfterChange(doc, change) {
6175 var out = [];
6176 for (var i = 0; i < doc.sel.ranges.length; i++) {
6177 var range = doc.sel.ranges[i];
6178 out.push(new Range(adjustForChange(range.anchor, change),
6179 adjustForChange(range.head, change)));
6180 }
6181 return normalizeSelection(out, doc.sel.primIndex)
6182 }
6183
6184 function offsetPos(pos, old, nw) {
6185 if (pos.line == old.line)
6186 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6187 else
6188 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6189 }
6190
6191 // Used by replaceSelections to allow moving the selection to the
6192 // start or around the replaced test. Hint may be "start" or "around".
6193 function computeReplacedSel(doc, changes, hint) {
6194 var out = [];
6195 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6196 for (var i = 0; i < changes.length; i++) {
6197 var change = changes[i];
6198 var from = offsetPos(change.from, oldPrev, newPrev);
6199 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6200 oldPrev = change.to;
6201 newPrev = to;
6202 if (hint == "around") {
6203 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6204 out[i] = new Range(inv ? to : from, inv ? from : to);
6205 } else {
6206 out[i] = new Range(from, from);
6207 }
6208 }
6209 return new Selection(out, doc.sel.primIndex)
6210 }
6211
6212 // Used to get the editor into a consistent state again when options change.
6213
6214 function loadMode(cm) {
6215 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6216 resetModeState(cm);
6217 }
6218
6219 function resetModeState(cm) {
6220 cm.doc.iter(function (line) {
6221 if (line.stateAfter) { line.stateAfter = null; }
6222 if (line.styles) { line.styles = null; }
6223 });
6224 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6225 startWorker(cm, 100);
6226 cm.state.modeGen++;
6227 if (cm.curOp) { regChange(cm); }
6228 }
6229
6230 // DOCUMENT DATA STRUCTURE
6231
6232 // By default, updates that start and end at the beginning of a line
6233 // are treated specially, in order to make the association of line
6234 // widgets and marker elements with the text behave more intuitive.
6235 function isWholeLineUpdate(doc, change) {
6236 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6237 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6238 }
6239
6240 // Perform a change on the document data structure.
6241 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6242 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6243 function update(line, text, spans) {
6244 updateLine(line, text, spans, estimateHeight$$1);
6245 signalLater(line, "change", line, change);
6246 }
6247 function linesFor(start, end) {
6248 var result = [];
6249 for (var i = start; i < end; ++i)
6250 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6251 return result
6252 }
6253
6254 var from = change.from, to = change.to, text = change.text;
6255 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6256 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6257
6258 // Adjust the line structure
6259 if (change.full) {
6260 doc.insert(0, linesFor(0, text.length));
6261 doc.remove(text.length, doc.size - text.length);
6262 } else if (isWholeLineUpdate(doc, change)) {
6263 // This is a whole-line replace. Treated specially to make
6264 // sure line objects move the way they are supposed to.
6265 var added = linesFor(0, text.length - 1);
6266 update(lastLine, lastLine.text, lastSpans);
6267 if (nlines) { doc.remove(from.line, nlines); }
6268 if (added.length) { doc.insert(from.line, added); }
6269 } else if (firstLine == lastLine) {
6270 if (text.length == 1) {
6271 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6272 } else {
6273 var added$1 = linesFor(1, text.length - 1);
6274 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6275 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6276 doc.insert(from.line + 1, added$1);
6277 }
6278 } else if (text.length == 1) {
6279 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6280 doc.remove(from.line + 1, nlines);
6281 } else {
6282 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6283 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6284 var added$2 = linesFor(1, text.length - 1);
6285 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6286 doc.insert(from.line + 1, added$2);
6287 }
6288
6289 signalLater(doc, "change", doc, change);
6290 }
6291
6292 // Call f for all linked documents.
6293 function linkedDocs(doc, f, sharedHistOnly) {
6294 function propagate(doc, skip, sharedHist) {
6295 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6296 var rel = doc.linked[i];
6297 if (rel.doc == skip) { continue }
6298 var shared = sharedHist && rel.sharedHist;
6299 if (sharedHistOnly && !shared) { continue }
6300 f(rel.doc, shared);
6301 propagate(rel.doc, doc, shared);
6302 } }
6303 }
6304 propagate(doc, null, true);
6305 }
6306
6307 // Attach a document to an editor.
6308 function attachDoc(cm, doc) {
6309 if (doc.cm) { throw new Error("This document is already in use.") }
6310 cm.doc = doc;
6311 doc.cm = cm;
6312 estimateLineHeights(cm);
6313 loadMode(cm);
6314 setDirectionClass(cm);
6315 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6316 cm.options.mode = doc.modeOption;
6317 regChange(cm);
6318 }
6319
6320 function setDirectionClass(cm) {
6321 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6322 }
6323
6324 function directionChanged(cm) {
6325 runInOp(cm, function () {
6326 setDirectionClass(cm);
6327 regChange(cm);
6328 });
6329 }
6330
6331 function History(startGen) {
6332 // Arrays of change events and selections. Doing something adds an
6333 // event to done and clears undo. Undoing moves events from done
6334 // to undone, redoing moves them in the other direction.
6335 this.done = []; this.undone = [];
6336 this.undoDepth = Infinity;
6337 // Used to track when changes can be merged into a single undo
6338 // event
6339 this.lastModTime = this.lastSelTime = 0;
6340 this.lastOp = this.lastSelOp = null;
6341 this.lastOrigin = this.lastSelOrigin = null;
6342 // Used by the isClean() method
6343 this.generation = this.maxGeneration = startGen || 1;
6344 }
6345
6346 // Create a history change event from an updateDoc-style change
6347 // object.
6348 function historyChangeFromChange(doc, change) {
6349 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6350 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6351 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6352 return histChange
6353 }
6354
6355 // Pop all selection events off the end of a history array. Stop at
6356 // a change event.
6357 function clearSelectionEvents(array) {
6358 while (array.length) {
6359 var last = lst(array);
6360 if (last.ranges) { array.pop(); }
6361 else { break }
6362 }
6363 }
6364
6365 // Find the top change event in the history. Pop off selection
6366 // events that are in the way.
6367 function lastChangeEvent(hist, force) {
6368 if (force) {
6369 clearSelectionEvents(hist.done);
6370 return lst(hist.done)
6371 } else if (hist.done.length && !lst(hist.done).ranges) {
6372 return lst(hist.done)
6373 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6374 hist.done.pop();
6375 return lst(hist.done)
6376 }
6377 }
6378
6379 // Register a change in the history. Merges changes that are within
6380 // a single operation, or are close together with an origin that
6381 // allows merging (starting with "+") into a single event.
6382 function addChangeToHistory(doc, change, selAfter, opId) {
6383 var hist = doc.history;
6384 hist.undone.length = 0;
6385 var time = +new Date, cur;
6386 var last;
6387
6388 if ((hist.lastOp == opId ||
6389 hist.lastOrigin == change.origin && change.origin &&
6390 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
6391 change.origin.charAt(0) == "*")) &&
6392 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6393 // Merge this change into the last event
6394 last = lst(cur.changes);
6395 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6396 // Optimized case for simple insertion -- don't want to add
6397 // new changesets for every character typed
6398 last.to = changeEnd(change);
6399 } else {
6400 // Add new sub-event
6401 cur.changes.push(historyChangeFromChange(doc, change));
6402 }
6403 } else {
6404 // Can not be merged, start a new event.
6405 var before = lst(hist.done);
6406 if (!before || !before.ranges)
6407 { pushSelectionToHistory(doc.sel, hist.done); }
6408 cur = {changes: [historyChangeFromChange(doc, change)],
6409 generation: hist.generation};
6410 hist.done.push(cur);
6411 while (hist.done.length > hist.undoDepth) {
6412 hist.done.shift();
6413 if (!hist.done[0].ranges) { hist.done.shift(); }
6414 }
6415 }
6416 hist.done.push(selAfter);
6417 hist.generation = ++hist.maxGeneration;
6418 hist.lastModTime = hist.lastSelTime = time;
6419 hist.lastOp = hist.lastSelOp = opId;
6420 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6421
6422 if (!last) { signal(doc, "historyAdded"); }
6423 }
6424
6425 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6426 var ch = origin.charAt(0);
6427 return ch == "*" ||
6428 ch == "+" &&
6429 prev.ranges.length == sel.ranges.length &&
6430 prev.somethingSelected() == sel.somethingSelected() &&
6431 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6432 }
6433
6434 // Called whenever the selection changes, sets the new selection as
6435 // the pending selection in the history, and pushes the old pending
6436 // selection into the 'done' array when it was significantly
6437 // different (in number of selected ranges, emptiness, or time).
6438 function addSelectionToHistory(doc, sel, opId, options) {
6439 var hist = doc.history, origin = options && options.origin;
6440
6441 // A new event is started when the previous origin does not match
6442 // the current, or the origins don't allow matching. Origins
6443 // starting with * are always merged, those starting with + are
6444 // merged when similar and close together in time.
6445 if (opId == hist.lastSelOp ||
6446 (origin && hist.lastSelOrigin == origin &&
6447 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6448 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6449 { hist.done[hist.done.length - 1] = sel; }
6450 else
6451 { pushSelectionToHistory(sel, hist.done); }
6452
6453 hist.lastSelTime = +new Date;
6454 hist.lastSelOrigin = origin;
6455 hist.lastSelOp = opId;
6456 if (options && options.clearRedo !== false)
6457 { clearSelectionEvents(hist.undone); }
6458 }
6459
6460 function pushSelectionToHistory(sel, dest) {
6461 var top = lst(dest);
6462 if (!(top && top.ranges && top.equals(sel)))
6463 { dest.push(sel); }
6464 }
6465
6466 // Used to store marked span information in the history.
6467 function attachLocalSpans(doc, change, from, to) {
6468 var existing = change["spans_" + doc.id], n = 0;
6469 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6470 if (line.markedSpans)
6471 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6472 ++n;
6473 });
6474 }
6475
6476 // When un/re-doing restores text containing marked spans, those
6477 // that have been explicitly cleared should not be restored.
6478 function removeClearedSpans(spans) {
6479 if (!spans) { return null }
6480 var out;
6481 for (var i = 0; i < spans.length; ++i) {
6482 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6483 else if (out) { out.push(spans[i]); }
6484 }
6485 return !out ? spans : out.length ? out : null
6486 }
6487
6488 // Retrieve and filter the old marked spans stored in a change event.
6489 function getOldSpans(doc, change) {
6490 var found = change["spans_" + doc.id];
6491 if (!found) { return null }
6492 var nw = [];
6493 for (var i = 0; i < change.text.length; ++i)
6494 { nw.push(removeClearedSpans(found[i])); }
6495 return nw
6496 }
6497
6498 // Used for un/re-doing changes from the history. Combines the
6499 // result of computing the existing spans with the set of spans that
6500 // existed in the history (so that deleting around a span and then
6501 // undoing brings back the span).
6502 function mergeOldSpans(doc, change) {
6503 var old = getOldSpans(doc, change);
6504 var stretched = stretchSpansOverChange(doc, change);
6505 if (!old) { return stretched }
6506 if (!stretched) { return old }
6507
6508 for (var i = 0; i < old.length; ++i) {
6509 var oldCur = old[i], stretchCur = stretched[i];
6510 if (oldCur && stretchCur) {
6511 spans: for (var j = 0; j < stretchCur.length; ++j) {
6512 var span = stretchCur[j];
6513 for (var k = 0; k < oldCur.length; ++k)
6514 { if (oldCur[k].marker == span.marker) { continue spans } }
6515 oldCur.push(span);
6516 }
6517 } else if (stretchCur) {
6518 old[i] = stretchCur;
6519 }
6520 }
6521 return old
6522 }
6523
6524 // Used both to provide a JSON-safe object in .getHistory, and, when
6525 // detaching a document, to split the history in two
6526 function copyHistoryArray(events, newGroup, instantiateSel) {
6527 var copy = [];
6528 for (var i = 0; i < events.length; ++i) {
6529 var event = events[i];
6530 if (event.ranges) {
6531 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6532 continue
6533 }
6534 var changes = event.changes, newChanges = [];
6535 copy.push({changes: newChanges});
6536 for (var j = 0; j < changes.length; ++j) {
6537 var change = changes[j], m = (void 0);
6538 newChanges.push({from: change.from, to: change.to, text: change.text});
6539 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6540 if (indexOf(newGroup, Number(m[1])) > -1) {
6541 lst(newChanges)[prop] = change[prop];
6542 delete change[prop];
6543 }
6544 } } }
6545 }
6546 }
6547 return copy
6548 }
6549
6550 // The 'scroll' parameter given to many of these indicated whether
6551 // the new cursor position should be scrolled into view after
6552 // modifying the selection.
6553
6554 // If shift is held or the extend flag is set, extends a range to
6555 // include a given position (and optionally a second position).
6556 // Otherwise, simply returns the range between the given positions.
6557 // Used for cursor motion and such.
6558 function extendRange(range, head, other, extend) {
6559 if (extend) {
6560 var anchor = range.anchor;
6561 if (other) {
6562 var posBefore = cmp(head, anchor) < 0;
6563 if (posBefore != (cmp(other, anchor) < 0)) {
6564 anchor = head;
6565 head = other;
6566 } else if (posBefore != (cmp(head, other) < 0)) {
6567 head = other;
6568 }
6569 }
6570 return new Range(anchor, head)
6571 } else {
6572 return new Range(other || head, head)
6573 }
6574 }
6575
6576 // Extend the primary selection range, discard the rest.
6577 function extendSelection(doc, head, other, options, extend) {
6578 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6579 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6580 }
6581
6582 // Extend all selections (pos is an array of selections with length
6583 // equal the number of selections)
6584 function extendSelections(doc, heads, options) {
6585 var out = [];
6586 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6587 for (var i = 0; i < doc.sel.ranges.length; i++)
6588 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6589 var newSel = normalizeSelection(out, doc.sel.primIndex);
6590 setSelection(doc, newSel, options);
6591 }
6592
6593 // Updates a single range in the selection.
6594 function replaceOneSelection(doc, i, range, options) {
6595 var ranges = doc.sel.ranges.slice(0);
6596 ranges[i] = range;
6597 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
6598 }
6599
6600 // Reset the selection to a single range.
6601 function setSimpleSelection(doc, anchor, head, options) {
6602 setSelection(doc, simpleSelection(anchor, head), options);
6603 }
6604
6605 // Give beforeSelectionChange handlers a change to influence a
6606 // selection update.
6607 function filterSelectionChange(doc, sel, options) {
6608 var obj = {
6609 ranges: sel.ranges,
6610 update: function(ranges) {
6611 var this$1 = this;
6612
6613 this.ranges = [];
6614 for (var i = 0; i < ranges.length; i++)
6615 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6616 clipPos(doc, ranges[i].head)); }
6617 },
6618 origin: options && options.origin
6619 };
6620 signal(doc, "beforeSelectionChange", doc, obj);
6621 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6622 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
6623 else { return sel }
6624 }
6625
6626 function setSelectionReplaceHistory(doc, sel, options) {
6627 var done = doc.history.done, last = lst(done);
6628 if (last && last.ranges) {
6629 done[done.length - 1] = sel;
6630 setSelectionNoUndo(doc, sel, options);
6631 } else {
6632 setSelection(doc, sel, options);
6633 }
6634 }
6635
6636 // Set a new selection.
6637 function setSelection(doc, sel, options) {
6638 setSelectionNoUndo(doc, sel, options);
6639 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6640 }
6641
6642 function setSelectionNoUndo(doc, sel, options) {
6643 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6644 { sel = filterSelectionChange(doc, sel, options); }
6645
6646 var bias = options && options.bias ||
6647 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6648 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6649
6650 if (!(options && options.scroll === false) && doc.cm)
6651 { ensureCursorVisible(doc.cm); }
6652 }
6653
6654 function setSelectionInner(doc, sel) {
6655 if (sel.equals(doc.sel)) { return }
6656
6657 doc.sel = sel;
6658
6659 if (doc.cm) {
6660 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
6661 signalCursorActivity(doc.cm);
6662 }
6663 signalLater(doc, "cursorActivity", doc);
6664 }
6665
6666 // Verify that the selection does not partially select any atomic
6667 // marked ranges.
6668 function reCheckSelection(doc) {
6669 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6670 }
6671
6672 // Return a selection that does not partially select any atomic
6673 // ranges.
6674 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6675 var out;
6676 for (var i = 0; i < sel.ranges.length; i++) {
6677 var range = sel.ranges[i];
6678 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6679 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6680 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6681 if (out || newAnchor != range.anchor || newHead != range.head) {
6682 if (!out) { out = sel.ranges.slice(0, i); }
6683 out[i] = new Range(newAnchor, newHead);
6684 }
6685 }
6686 return out ? normalizeSelection(out, sel.primIndex) : sel
6687 }
6688
6689 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6690 var line = getLine(doc, pos.line);
6691 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6692 var sp = line.markedSpans[i], m = sp.marker;
6693 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6694 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6695 if (mayClear) {
6696 signal(m, "beforeCursorEnter");
6697 if (m.explicitlyCleared) {
6698 if (!line.markedSpans) { break }
6699 else {--i; continue}
6700 }
6701 }
6702 if (!m.atomic) { continue }
6703
6704 if (oldPos) {
6705 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6706 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6707 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6708 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6709 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6710 }
6711
6712 var far = m.find(dir < 0 ? -1 : 1);
6713 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6714 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6715 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6716 }
6717 } }
6718 return pos
6719 }
6720
6721 // Ensure a given position is not inside an atomic range.
6722 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6723 var dir = bias || 1;
6724 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6725 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6726 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6727 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6728 if (!found) {
6729 doc.cantEdit = true;
6730 return Pos(doc.first, 0)
6731 }
6732 return found
6733 }
6734
6735 function movePos(doc, pos, dir, line) {
6736 if (dir < 0 && pos.ch == 0) {
6737 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6738 else { return null }
6739 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6740 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6741 else { return null }
6742 } else {
6743 return new Pos(pos.line, pos.ch + dir)
6744 }
6745 }
6746
6747 function selectAll(cm) {
6748 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6749 }
6750
6751 // UPDATING
6752
6753 // Allow "beforeChange" event handlers to influence a change
6754 function filterChange(doc, change, update) {
6755 var obj = {
6756 canceled: false,
6757 from: change.from,
6758 to: change.to,
6759 text: change.text,
6760 origin: change.origin,
6761 cancel: function () { return obj.canceled = true; }
6762 };
6763 if (update) { obj.update = function (from, to, text, origin) {
6764 if (from) { obj.from = clipPos(doc, from); }
6765 if (to) { obj.to = clipPos(doc, to); }
6766 if (text) { obj.text = text; }
6767 if (origin !== undefined) { obj.origin = origin; }
6768 }; }
6769 signal(doc, "beforeChange", doc, obj);
6770 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6771
6772 if (obj.canceled) { return null }
6773 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6774 }
6775
6776 // Apply a change to a document, and add it to the document's
6777 // history, and propagating it to all linked documents.
6778 function makeChange(doc, change, ignoreReadOnly) {
6779 if (doc.cm) {
6780 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6781 if (doc.cm.state.suppressEdits) { return }
6782 }
6783
6784 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6785 change = filterChange(doc, change, true);
6786 if (!change) { return }
6787 }
6788
6789 // Possibly split or suppress the update based on the presence
6790 // of read-only spans in its range.
6791 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6792 if (split) {
6793 for (var i = split.length - 1; i >= 0; --i)
6794 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6795 } else {
6796 makeChangeInner(doc, change);
6797 }
6798 }
6799
6800 function makeChangeInner(doc, change) {
6801 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6802 var selAfter = computeSelAfterChange(doc, change);
6803 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6804
6805 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6806 var rebased = [];
6807
6808 linkedDocs(doc, function (doc, sharedHist) {
6809 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6810 rebaseHist(doc.history, change);
6811 rebased.push(doc.history);
6812 }
6813 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6814 });
6815 }
6816
6817 // Revert a change stored in a document's history.
6818 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6819 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
6820
6821 var hist = doc.history, event, selAfter = doc.sel;
6822 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6823
6824 // Verify that there is a useable event (so that ctrl-z won't
6825 // needlessly clear selection events)
6826 var i = 0;
6827 for (; i < source.length; i++) {
6828 event = source[i];
6829 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6830 { break }
6831 }
6832 if (i == source.length) { return }
6833 hist.lastOrigin = hist.lastSelOrigin = null;
6834
6835 for (;;) {
6836 event = source.pop();
6837 if (event.ranges) {
6838 pushSelectionToHistory(event, dest);
6839 if (allowSelectionOnly && !event.equals(doc.sel)) {
6840 setSelection(doc, event, {clearRedo: false});
6841 return
6842 }
6843 selAfter = event;
6844 }
6845 else { break }
6846 }
6847
6848 // Build up a reverse change object to add to the opposite history
6849 // stack (redo when undoing, and vice versa).
6850 var antiChanges = [];
6851 pushSelectionToHistory(selAfter, dest);
6852 dest.push({changes: antiChanges, generation: hist.generation});
6853 hist.generation = event.generation || ++hist.maxGeneration;
6854
6855 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6856
6857 var loop = function ( i ) {
6858 var change = event.changes[i];
6859 change.origin = type;
6860 if (filter && !filterChange(doc, change, false)) {
6861 source.length = 0;
6862 return {}
6863 }
6864
6865 antiChanges.push(historyChangeFromChange(doc, change));
6866
6867 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6868 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6869 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6870 var rebased = [];
6871
6872 // Propagate to the linked documents
6873 linkedDocs(doc, function (doc, sharedHist) {
6874 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6875 rebaseHist(doc.history, change);
6876 rebased.push(doc.history);
6877 }
6878 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6879 });
6880 };
6881
6882 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6883 var returned = loop( i$1 );
6884
6885 if ( returned ) return returned.v;
6886 }
6887 }
6888
6889 // Sub-views need their line numbers shifted when text is added
6890 // above or below them in the parent document.
6891 function shiftDoc(doc, distance) {
6892 if (distance == 0) { return }
6893 doc.first += distance;
6894 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6895 Pos(range.anchor.line + distance, range.anchor.ch),
6896 Pos(range.head.line + distance, range.head.ch)
6897 ); }), doc.sel.primIndex);
6898 if (doc.cm) {
6899 regChange(doc.cm, doc.first, doc.first - distance, distance);
6900 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6901 { regLineChange(doc.cm, l, "gutter"); }
6902 }
6903 }
6904
6905 // More lower-level change function, handling only a single document
6906 // (not linked ones).
6907 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6908 if (doc.cm && !doc.cm.curOp)
6909 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6910
6911 if (change.to.line < doc.first) {
6912 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6913 return
6914 }
6915 if (change.from.line > doc.lastLine()) { return }
6916
6917 // Clip the change to the size of this doc
6918 if (change.from.line < doc.first) {
6919 var shift = change.text.length - 1 - (doc.first - change.from.line);
6920 shiftDoc(doc, shift);
6921 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6922 text: [lst(change.text)], origin: change.origin};
6923 }
6924 var last = doc.lastLine();
6925 if (change.to.line > last) {
6926 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6927 text: [change.text[0]], origin: change.origin};
6928 }
6929
6930 change.removed = getBetween(doc, change.from, change.to);
6931
6932 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6933 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6934 else { updateDoc(doc, change, spans); }
6935 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6936 }
6937
6938 // Handle the interaction of a change to a document with the editor
6939 // that this document is part of.
6940 function makeChangeSingleDocInEditor(cm, change, spans) {
6941 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6942
6943 var recomputeMaxLength = false, checkWidthStart = from.line;
6944 if (!cm.options.lineWrapping) {
6945 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6946 doc.iter(checkWidthStart, to.line + 1, function (line) {
6947 if (line == display.maxLine) {
6948 recomputeMaxLength = true;
6949 return true
6950 }
6951 });
6952 }
6953
6954 if (doc.sel.contains(change.from, change.to) > -1)
6955 { signalCursorActivity(cm); }
6956
6957 updateDoc(doc, change, spans, estimateHeight(cm));
6958
6959 if (!cm.options.lineWrapping) {
6960 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
6961 var len = lineLength(line);
6962 if (len > display.maxLineLength) {
6963 display.maxLine = line;
6964 display.maxLineLength = len;
6965 display.maxLineChanged = true;
6966 recomputeMaxLength = false;
6967 }
6968 });
6969 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
6970 }
6971
6972 retreatFrontier(doc, from.line);
6973 startWorker(cm, 400);
6974
6975 var lendiff = change.text.length - (to.line - from.line) - 1;
6976 // Remember that these lines changed, for updating the display
6977 if (change.full)
6978 { regChange(cm); }
6979 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
6980 { regLineChange(cm, from.line, "text"); }
6981 else
6982 { regChange(cm, from.line, to.line + 1, lendiff); }
6983
6984 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
6985 if (changeHandler || changesHandler) {
6986 var obj = {
6987 from: from, to: to,
6988 text: change.text,
6989 removed: change.removed,
6990 origin: change.origin
6991 };
6992 if (changeHandler) { signalLater(cm, "change", cm, obj); }
6993 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
6994 }
6995 cm.display.selForContextMenu = null;
6996 }
6997
6998 function replaceRange(doc, code, from, to, origin) {
6999 if (!to) { to = from; }
7000 if (cmp(to, from) < 0) { var assign;
7001 (assign = [to, from], from = assign[0], to = assign[1], assign); }
7002 if (typeof code == "string") { code = doc.splitLines(code); }
7003 makeChange(doc, {from: from, to: to, text: code, origin: origin});
7004 }
7005
7006 // Rebasing/resetting history to deal with externally-sourced changes
7007
7008 function rebaseHistSelSingle(pos, from, to, diff) {
7009 if (to < pos.line) {
7010 pos.line += diff;
7011 } else if (from < pos.line) {
7012 pos.line = from;
7013 pos.ch = 0;
7014 }
7015 }
7016
7017 // Tries to rebase an array of history events given a change in the
7018 // document. If the change touches the same lines as the event, the
7019 // event, and everything 'behind' it, is discarded. If the change is
7020 // before the event, the event's positions are updated. Uses a
7021 // copy-on-write scheme for the positions, to avoid having to
7022 // reallocate them all on every rebase, but also avoid problems with
7023 // shared position objects being unsafely updated.
7024 function rebaseHistArray(array, from, to, diff) {
7025 for (var i = 0; i < array.length; ++i) {
7026 var sub = array[i], ok = true;
7027 if (sub.ranges) {
7028 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7029 for (var j = 0; j < sub.ranges.length; j++) {
7030 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7031 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7032 }
7033 continue
7034 }
7035 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
7036 var cur = sub.changes[j$1];
7037 if (to < cur.from.line) {
7038 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7039 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7040 } else if (from <= cur.to.line) {
7041 ok = false;
7042 break
7043 }
7044 }
7045 if (!ok) {
7046 array.splice(0, i + 1);
7047 i = 0;
7048 }
7049 }
7050 }
7051
7052 function rebaseHist(hist, change) {
7053 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7054 rebaseHistArray(hist.done, from, to, diff);
7055 rebaseHistArray(hist.undone, from, to, diff);
7056 }
7057
7058 // Utility for applying a change to a line by handle or number,
7059 // returning the number and optionally registering the line as
7060 // changed.
7061 function changeLine(doc, handle, changeType, op) {
7062 var no = handle, line = handle;
7063 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
7064 else { no = lineNo(handle); }
7065 if (no == null) { return null }
7066 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
7067 return line
7068 }
7069
7070 // The document is represented as a BTree consisting of leaves, with
7071 // chunk of lines in them, and branches, with up to ten leaves or
7072 // other branch nodes below them. The top node is always a branch
7073 // node, and is the document object itself (meaning it has
7074 // additional methods and properties).
7075 //
7076 // All nodes have parent links. The tree is used both to go from
7077 // line numbers to line objects, and to go from objects to numbers.
7078 // It also indexes by height, and is used to convert between height
7079 // and line object, and to find the total height of the document.
7080 //
7081 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7082
7083 function LeafChunk(lines) {
7084 var this$1 = this;
7085
7086 this.lines = lines;
7087 this.parent = null;
7088 var height = 0;
7089 for (var i = 0; i < lines.length; ++i) {
7090 lines[i].parent = this$1;
7091 height += lines[i].height;
7092 }
7093 this.height = height;
7094 }
7095
7096 LeafChunk.prototype = {
7097 chunkSize: function chunkSize() { return this.lines.length },
7098
7099 // Remove the n lines at offset 'at'.
7100 removeInner: function removeInner(at, n) {
7101 var this$1 = this;
7102
7103 for (var i = at, e = at + n; i < e; ++i) {
7104 var line = this$1.lines[i];
7105 this$1.height -= line.height;
7106 cleanUpLine(line);
7107 signalLater(line, "delete");
7108 }
7109 this.lines.splice(at, n);
7110 },
7111
7112 // Helper used to collapse a small branch into a single leaf.
7113 collapse: function collapse(lines) {
7114 lines.push.apply(lines, this.lines);
7115 },
7116
7117 // Insert the given array of lines at offset 'at', count them as
7118 // having the given height.
7119 insertInner: function insertInner(at, lines, height) {
7120 var this$1 = this;
7121
7122 this.height += height;
7123 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7124 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
7125 },
7126
7127 // Used to iterate over a part of the tree.
7128 iterN: function iterN(at, n, op) {
7129 var this$1 = this;
7130
7131 for (var e = at + n; at < e; ++at)
7132 { if (op(this$1.lines[at])) { return true } }
7133 }
7134 };
7135
7136 function BranchChunk(children) {
7137 var this$1 = this;
7138
7139 this.children = children;
7140 var size = 0, height = 0;
7141 for (var i = 0; i < children.length; ++i) {
7142 var ch = children[i];
7143 size += ch.chunkSize(); height += ch.height;
7144 ch.parent = this$1;
7145 }
7146 this.size = size;
7147 this.height = height;
7148 this.parent = null;
7149 }
7150
7151 BranchChunk.prototype = {
7152 chunkSize: function chunkSize() { return this.size },
7153
7154 removeInner: function removeInner(at, n) {
7155 var this$1 = this;
7156
7157 this.size -= n;
7158 for (var i = 0; i < this.children.length; ++i) {
7159 var child = this$1.children[i], sz = child.chunkSize();
7160 if (at < sz) {
7161 var rm = Math.min(n, sz - at), oldHeight = child.height;
7162 child.removeInner(at, rm);
7163 this$1.height -= oldHeight - child.height;
7164 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
7165 if ((n -= rm) == 0) { break }
7166 at = 0;
7167 } else { at -= sz; }
7168 }
7169 // If the result is smaller than 25 lines, ensure that it is a
7170 // single leaf node.
7171 if (this.size - n < 25 &&
7172 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7173 var lines = [];
7174 this.collapse(lines);
7175 this.children = [new LeafChunk(lines)];
7176 this.children[0].parent = this;
7177 }
7178 },
7179
7180 collapse: function collapse(lines) {
7181 var this$1 = this;
7182
7183 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
7184 },
7185
7186 insertInner: function insertInner(at, lines, height) {
7187 var this$1 = this;
7188
7189 this.size += lines.length;
7190 this.height += height;
7191 for (var i = 0; i < this.children.length; ++i) {
7192 var child = this$1.children[i], sz = child.chunkSize();
7193 if (at <= sz) {
7194 child.insertInner(at, lines, height);
7195 if (child.lines && child.lines.length > 50) {
7196 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7197 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7198 var remaining = child.lines.length % 25 + 25;
7199 for (var pos = remaining; pos < child.lines.length;) {
7200 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7201 child.height -= leaf.height;
7202 this$1.children.splice(++i, 0, leaf);
7203 leaf.parent = this$1;
7204 }
7205 child.lines = child.lines.slice(0, remaining);
7206 this$1.maybeSpill();
7207 }
7208 break
7209 }
7210 at -= sz;
7211 }
7212 },
7213
7214 // When a node has grown, check whether it should be split.
7215 maybeSpill: function maybeSpill() {
7216 if (this.children.length <= 10) { return }
7217 var me = this;
7218 do {
7219 var spilled = me.children.splice(me.children.length - 5, 5);
7220 var sibling = new BranchChunk(spilled);
7221 if (!me.parent) { // Become the parent node
7222 var copy = new BranchChunk(me.children);
7223 copy.parent = me;
7224 me.children = [copy, sibling];
7225 me = copy;
7226 } else {
7227 me.size -= sibling.size;
7228 me.height -= sibling.height;
7229 var myIndex = indexOf(me.parent.children, me);
7230 me.parent.children.splice(myIndex + 1, 0, sibling);
7231 }
7232 sibling.parent = me.parent;
7233 } while (me.children.length > 10)
7234 me.parent.maybeSpill();
7235 },
7236
7237 iterN: function iterN(at, n, op) {
7238 var this$1 = this;
7239
7240 for (var i = 0; i < this.children.length; ++i) {
7241 var child = this$1.children[i], sz = child.chunkSize();
7242 if (at < sz) {
7243 var used = Math.min(n, sz - at);
7244 if (child.iterN(at, used, op)) { return true }
7245 if ((n -= used) == 0) { break }
7246 at = 0;
7247 } else { at -= sz; }
7248 }
7249 }
7250 };
7251
7252 // Line widgets are block elements displayed above or below a line.
7253
7254 var LineWidget = function(doc, node, options) {
7255 var this$1 = this;
7256
7257 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7258 { this$1[opt] = options[opt]; } } }
7259 this.doc = doc;
7260 this.node = node;
7261 };
7262
7263 LineWidget.prototype.clear = function () {
7264 var this$1 = this;
7265
7266 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7267 if (no == null || !ws) { return }
7268 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7269 if (!ws.length) { line.widgets = null; }
7270 var height = widgetHeight(this);
7271 updateLineHeight(line, Math.max(0, line.height - height));
7272 if (cm) {
7273 runInOp(cm, function () {
7274 adjustScrollWhenAboveVisible(cm, line, -height);
7275 regLineChange(cm, no, "widget");
7276 });
7277 signalLater(cm, "lineWidgetCleared", cm, this, no);
7278 }
7279 };
7280
7281 LineWidget.prototype.changed = function () {
7282 var this$1 = this;
7283
7284 var oldH = this.height, cm = this.doc.cm, line = this.line;
7285 this.height = null;
7286 var diff = widgetHeight(this) - oldH;
7287 if (!diff) { return }
7288 updateLineHeight(line, line.height + diff);
7289 if (cm) {
7290 runInOp(cm, function () {
7291 cm.curOp.forceUpdate = true;
7292 adjustScrollWhenAboveVisible(cm, line, diff);
7293 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7294 });
7295 }
7296 };
7297 eventMixin(LineWidget);
7298
7299 function adjustScrollWhenAboveVisible(cm, line, diff) {
7300 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7301 { addToScrollTop(cm, diff); }
7302 }
7303
7304 function addLineWidget(doc, handle, node, options) {
7305 var widget = new LineWidget(doc, node, options);
7306 var cm = doc.cm;
7307 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7308 changeLine(doc, handle, "widget", function (line) {
7309 var widgets = line.widgets || (line.widgets = []);
7310 if (widget.insertAt == null) { widgets.push(widget); }
7311 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7312 widget.line = line;
7313 if (cm && !lineIsHidden(doc, line)) {
7314 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7315 updateLineHeight(line, line.height + widgetHeight(widget));
7316 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7317 cm.curOp.forceUpdate = true;
7318 }
7319 return true
7320 });
7321 signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle));
7322 return widget
7323 }
7324
7325 // TEXTMARKERS
7326
7327 // Created with markText and setBookmark methods. A TextMarker is a
7328 // handle that can be used to clear or find a marked position in the
7329 // document. Line objects hold arrays (markedSpans) containing
7330 // {from, to, marker} object pointing to such marker objects, and
7331 // indicating that such a marker is present on that line. Multiple
7332 // lines may point to the same marker when it spans across lines.
7333 // The spans will have null for their from/to properties when the
7334 // marker continues beyond the start/end of the line. Markers have
7335 // links back to the lines they currently touch.
7336
7337 // Collapsed markers have unique ids, in order to be able to order
7338 // them, which is needed for uniquely determining an outer marker
7339 // when they overlap (they may nest, but not partially overlap).
7340 var nextMarkerId = 0;
7341
7342 var TextMarker = function(doc, type) {
7343 this.lines = [];
7344 this.type = type;
7345 this.doc = doc;
7346 this.id = ++nextMarkerId;
7347 };
7348
7349 // Clear the marker.
7350 TextMarker.prototype.clear = function () {
7351 var this$1 = this;
7352
7353 if (this.explicitlyCleared) { return }
7354 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7355 if (withOp) { startOperation(cm); }
7356 if (hasHandler(this, "clear")) {
7357 var found = this.find();
7358 if (found) { signalLater(this, "clear", found.from, found.to); }
7359 }
7360 var min = null, max = null;
7361 for (var i = 0; i < this.lines.length; ++i) {
7362 var line = this$1.lines[i];
7363 var span = getMarkedSpanFor(line.markedSpans, this$1);
7364 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7365 else if (cm) {
7366 if (span.to != null) { max = lineNo(line); }
7367 if (span.from != null) { min = lineNo(line); }
7368 }
7369 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7370 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7371 { updateLineHeight(line, textHeight(cm.display)); }
7372 }
7373 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7374 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7375 if (len > cm.display.maxLineLength) {
7376 cm.display.maxLine = visual;
7377 cm.display.maxLineLength = len;
7378 cm.display.maxLineChanged = true;
7379 }
7380 } }
7381
7382 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7383 this.lines.length = 0;
7384 this.explicitlyCleared = true;
7385 if (this.atomic && this.doc.cantEdit) {
7386 this.doc.cantEdit = false;
7387 if (cm) { reCheckSelection(cm.doc); }
7388 }
7389 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7390 if (withOp) { endOperation(cm); }
7391 if (this.parent) { this.parent.clear(); }
7392 };
7393
7394 // Find the position of the marker in the document. Returns a {from,
7395 // to} object by default. Side can be passed to get a specific side
7396 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7397 // Pos objects returned contain a line object, rather than a line
7398 // number (used to prevent looking up the same line twice).
7399 TextMarker.prototype.find = function (side, lineObj) {
7400 var this$1 = this;
7401
7402 if (side == null && this.type == "bookmark") { side = 1; }
7403 var from, to;
7404 for (var i = 0; i < this.lines.length; ++i) {
7405 var line = this$1.lines[i];
7406 var span = getMarkedSpanFor(line.markedSpans, this$1);
7407 if (span.from != null) {
7408 from = Pos(lineObj ? line : lineNo(line), span.from);
7409 if (side == -1) { return from }
7410 }
7411 if (span.to != null) {
7412 to = Pos(lineObj ? line : lineNo(line), span.to);
7413 if (side == 1) { return to }
7414 }
7415 }
7416 return from && {from: from, to: to}
7417 };
7418
7419 // Signals that the marker's widget changed, and surrounding layout
7420 // should be recomputed.
7421 TextMarker.prototype.changed = function () {
7422 var this$1 = this;
7423
7424 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7425 if (!pos || !cm) { return }
7426 runInOp(cm, function () {
7427 var line = pos.line, lineN = lineNo(pos.line);
7428 var view = findViewForLine(cm, lineN);
7429 if (view) {
7430 clearLineMeasurementCacheFor(view);
7431 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7432 }
7433 cm.curOp.updateMaxLine = true;
7434 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7435 var oldHeight = widget.height;
7436 widget.height = null;
7437 var dHeight = widgetHeight(widget) - oldHeight;
7438 if (dHeight)
7439 { updateLineHeight(line, line.height + dHeight); }
7440 }
7441 signalLater(cm, "markerChanged", cm, this$1);
7442 });
7443 };
7444
7445 TextMarker.prototype.attachLine = function (line) {
7446 if (!this.lines.length && this.doc.cm) {
7447 var op = this.doc.cm.curOp;
7448 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7449 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7450 }
7451 this.lines.push(line);
7452 };
7453
7454 TextMarker.prototype.detachLine = function (line) {
7455 this.lines.splice(indexOf(this.lines, line), 1);
7456 if (!this.lines.length && this.doc.cm) {
7457 var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7458 }
7459 };
7460 eventMixin(TextMarker);
7461
7462 // Create a marker, wire it up to the right lines, and
7463 function markText(doc, from, to, options, type) {
7464 // Shared markers (across linked documents) are handled separately
7465 // (markTextShared will call out to this again, once per
7466 // document).
7467 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7468 // Ensure we are in an operation.
7469 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7470
7471 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7472 if (options) { copyObj(options, marker, false); }
7473 // Don't connect empty markers unless clearWhenEmpty is false
7474 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7475 { return marker }
7476 if (marker.replacedWith) {
7477 // Showing up as a widget implies collapsed (widget replaces text)
7478 marker.collapsed = true;
7479 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7480 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7481 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7482 }
7483 if (marker.collapsed) {
7484 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7485 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7486 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7487 seeCollapsedSpans();
7488 }
7489
7490 if (marker.addToHistory)
7491 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7492
7493 var curLine = from.line, cm = doc.cm, updateMaxLine;
7494 doc.iter(curLine, to.line + 1, function (line) {
7495 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7496 { updateMaxLine = true; }
7497 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7498 addMarkedSpan(line, new MarkedSpan(marker,
7499 curLine == from.line ? from.ch : null,
7500 curLine == to.line ? to.ch : null));
7501 ++curLine;
7502 });
7503 // lineIsHidden depends on the presence of the spans, so needs a second pass
7504 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7505 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7506 }); }
7507
7508 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7509
7510 if (marker.readOnly) {
7511 seeReadOnlySpans();
7512 if (doc.history.done.length || doc.history.undone.length)
7513 { doc.clearHistory(); }
7514 }
7515 if (marker.collapsed) {
7516 marker.id = ++nextMarkerId;
7517 marker.atomic = true;
7518 }
7519 if (cm) {
7520 // Sync editor state
7521 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7522 if (marker.collapsed)
7523 { regChange(cm, from.line, to.line + 1); }
7524 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
7525 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7526 if (marker.atomic) { reCheckSelection(cm.doc); }
7527 signalLater(cm, "markerAdded", cm, marker);
7528 }
7529 return marker
7530 }
7531
7532 // SHARED TEXTMARKERS
7533
7534 // A shared marker spans multiple linked documents. It is
7535 // implemented as a meta-marker-object controlling multiple normal
7536 // markers.
7537 var SharedTextMarker = function(markers, primary) {
7538 var this$1 = this;
7539
7540 this.markers = markers;
7541 this.primary = primary;
7542 for (var i = 0; i < markers.length; ++i)
7543 { markers[i].parent = this$1; }
7544 };
7545
7546 SharedTextMarker.prototype.clear = function () {
7547 var this$1 = this;
7548
7549 if (this.explicitlyCleared) { return }
7550 this.explicitlyCleared = true;
7551 for (var i = 0; i < this.markers.length; ++i)
7552 { this$1.markers[i].clear(); }
7553 signalLater(this, "clear");
7554 };
7555
7556 SharedTextMarker.prototype.find = function (side, lineObj) {
7557 return this.primary.find(side, lineObj)
7558 };
7559 eventMixin(SharedTextMarker);
7560
7561 function markTextShared(doc, from, to, options, type) {
7562 options = copyObj(options);
7563 options.shared = false;
7564 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7565 var widget = options.widgetNode;
7566 linkedDocs(doc, function (doc) {
7567 if (widget) { options.widgetNode = widget.cloneNode(true); }
7568 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7569 for (var i = 0; i < doc.linked.length; ++i)
7570 { if (doc.linked[i].isParent) { return } }
7571 primary = lst(markers);
7572 });
7573 return new SharedTextMarker(markers, primary)
7574 }
7575
7576 function findSharedMarkers(doc) {
7577 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7578 }
7579
7580 function copySharedMarkers(doc, markers) {
7581 for (var i = 0; i < markers.length; i++) {
7582 var marker = markers[i], pos = marker.find();
7583 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7584 if (cmp(mFrom, mTo)) {
7585 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7586 marker.markers.push(subMark);
7587 subMark.parent = marker;
7588 }
7589 }
7590 }
7591
7592 function detachSharedMarkers(markers) {
7593 var loop = function ( i ) {
7594 var marker = markers[i], linked = [marker.primary.doc];
7595 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7596 for (var j = 0; j < marker.markers.length; j++) {
7597 var subMarker = marker.markers[j];
7598 if (indexOf(linked, subMarker.doc) == -1) {
7599 subMarker.parent = null;
7600 marker.markers.splice(j--, 1);
7601 }
7602 }
7603 };
7604
7605 for (var i = 0; i < markers.length; i++) loop( i );
7606 }
7607
7608 var nextDocId = 0;
7609 var Doc = function(text, mode, firstLine, lineSep, direction) {
7610 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7611 if (firstLine == null) { firstLine = 0; }
7612
7613 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7614 this.first = firstLine;
7615 this.scrollTop = this.scrollLeft = 0;
7616 this.cantEdit = false;
7617 this.cleanGeneration = 1;
7618 this.modeFrontier = this.highlightFrontier = firstLine;
7619 var start = Pos(firstLine, 0);
7620 this.sel = simpleSelection(start);
7621 this.history = new History(null);
7622 this.id = ++nextDocId;
7623 this.modeOption = mode;
7624 this.lineSep = lineSep;
7625 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7626 this.extend = false;
7627
7628 if (typeof text == "string") { text = this.splitLines(text); }
7629 updateDoc(this, {from: start, to: start, text: text});
7630 setSelection(this, simpleSelection(start), sel_dontScroll);
7631 };
7632
7633 Doc.prototype = createObj(BranchChunk.prototype, {
7634 constructor: Doc,
7635 // Iterate over the document. Supports two forms -- with only one
7636 // argument, it calls that for each line in the document. With
7637 // three, it iterates over the range given by the first two (with
7638 // the second being non-inclusive).
7639 iter: function(from, to, op) {
7640 if (op) { this.iterN(from - this.first, to - from, op); }
7641 else { this.iterN(this.first, this.first + this.size, from); }
7642 },
7643
7644 // Non-public interface for adding and removing lines.
7645 insert: function(at, lines) {
7646 var height = 0;
7647 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7648 this.insertInner(at - this.first, lines, height);
7649 },
7650 remove: function(at, n) { this.removeInner(at - this.first, n); },
7651
7652 // From here, the methods are part of the public interface. Most
7653 // are also available from CodeMirror (editor) instances.
7654
7655 getValue: function(lineSep) {
7656 var lines = getLines(this, this.first, this.first + this.size);
7657 if (lineSep === false) { return lines }
7658 return lines.join(lineSep || this.lineSeparator())
7659 },
7660 setValue: docMethodOp(function(code) {
7661 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7662 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7663 text: this.splitLines(code), origin: "setValue", full: true}, true);
7664 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7665 setSelection(this, simpleSelection(top), sel_dontScroll);
7666 }),
7667 replaceRange: function(code, from, to, origin) {
7668 from = clipPos(this, from);
7669 to = to ? clipPos(this, to) : from;
7670 replaceRange(this, code, from, to, origin);
7671 },
7672 getRange: function(from, to, lineSep) {
7673 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7674 if (lineSep === false) { return lines }
7675 return lines.join(lineSep || this.lineSeparator())
7676 },
7677
7678 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7679
7680 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7681 getLineNumber: function(line) {return lineNo(line)},
7682
7683 getLineHandleVisualStart: function(line) {
7684 if (typeof line == "number") { line = getLine(this, line); }
7685 return visualLine(line)
7686 },
7687
7688 lineCount: function() {return this.size},
7689 firstLine: function() {return this.first},
7690 lastLine: function() {return this.first + this.size - 1},
7691
7692 clipPos: function(pos) {return clipPos(this, pos)},
7693
7694 getCursor: function(start) {
7695 var range$$1 = this.sel.primary(), pos;
7696 if (start == null || start == "head") { pos = range$$1.head; }
7697 else if (start == "anchor") { pos = range$$1.anchor; }
7698 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7699 else { pos = range$$1.from(); }
7700 return pos
7701 },
7702 listSelections: function() { return this.sel.ranges },
7703 somethingSelected: function() {return this.sel.somethingSelected()},
7704
7705 setCursor: docMethodOp(function(line, ch, options) {
7706 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7707 }),
7708 setSelection: docMethodOp(function(anchor, head, options) {
7709 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7710 }),
7711 extendSelection: docMethodOp(function(head, other, options) {
7712 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7713 }),
7714 extendSelections: docMethodOp(function(heads, options) {
7715 extendSelections(this, clipPosArray(this, heads), options);
7716 }),
7717 extendSelectionsBy: docMethodOp(function(f, options) {
7718 var heads = map(this.sel.ranges, f);
7719 extendSelections(this, clipPosArray(this, heads), options);
7720 }),
7721 setSelections: docMethodOp(function(ranges, primary, options) {
7722 var this$1 = this;
7723
7724 if (!ranges.length) { return }
7725 var out = [];
7726 for (var i = 0; i < ranges.length; i++)
7727 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7728 clipPos(this$1, ranges[i].head)); }
7729 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7730 setSelection(this, normalizeSelection(out, primary), options);
7731 }),
7732 addSelection: docMethodOp(function(anchor, head, options) {
7733 var ranges = this.sel.ranges.slice(0);
7734 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7735 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7736 }),
7737
7738 getSelection: function(lineSep) {
7739 var this$1 = this;
7740
7741 var ranges = this.sel.ranges, lines;
7742 for (var i = 0; i < ranges.length; i++) {
7743 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7744 lines = lines ? lines.concat(sel) : sel;
7745 }
7746 if (lineSep === false) { return lines }
7747 else { return lines.join(lineSep || this.lineSeparator()) }
7748 },
7749 getSelections: function(lineSep) {
7750 var this$1 = this;
7751
7752 var parts = [], ranges = this.sel.ranges;
7753 for (var i = 0; i < ranges.length; i++) {
7754 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7755 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7756 parts[i] = sel;
7757 }
7758 return parts
7759 },
7760 replaceSelection: function(code, collapse, origin) {
7761 var dup = [];
7762 for (var i = 0; i < this.sel.ranges.length; i++)
7763 { dup[i] = code; }
7764 this.replaceSelections(dup, collapse, origin || "+input");
7765 },
7766 replaceSelections: docMethodOp(function(code, collapse, origin) {
7767 var this$1 = this;
7768
7769 var changes = [], sel = this.sel;
7770 for (var i = 0; i < sel.ranges.length; i++) {
7771 var range$$1 = sel.ranges[i];
7772 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7773 }
7774 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7775 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7776 { makeChange(this$1, changes[i$1]); }
7777 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7778 else if (this.cm) { ensureCursorVisible(this.cm); }
7779 }),
7780 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7781 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7782 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7783 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7784
7785 setExtending: function(val) {this.extend = val;},
7786 getExtending: function() {return this.extend},
7787
7788 historySize: function() {
7789 var hist = this.history, done = 0, undone = 0;
7790 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7791 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7792 return {undo: done, redo: undone}
7793 },
7794 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7795
7796 markClean: function() {
7797 this.cleanGeneration = this.changeGeneration(true);
7798 },
7799 changeGeneration: function(forceSplit) {
7800 if (forceSplit)
7801 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7802 return this.history.generation
7803 },
7804 isClean: function (gen) {
7805 return this.history.generation == (gen || this.cleanGeneration)
7806 },
7807
7808 getHistory: function() {
7809 return {done: copyHistoryArray(this.history.done),
7810 undone: copyHistoryArray(this.history.undone)}
7811 },
7812 setHistory: function(histData) {
7813 var hist = this.history = new History(this.history.maxGeneration);
7814 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7815 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7816 },
7817
7818 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7819 return changeLine(this, line, "gutter", function (line) {
7820 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7821 markers[gutterID] = value;
7822 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7823 return true
7824 })
7825 }),
7826
7827 clearGutter: docMethodOp(function(gutterID) {
7828 var this$1 = this;
7829
7830 this.iter(function (line) {
7831 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7832 changeLine(this$1, line, "gutter", function () {
7833 line.gutterMarkers[gutterID] = null;
7834 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7835 return true
7836 });
7837 }
7838 });
7839 }),
7840
7841 lineInfo: function(line) {
7842 var n;
7843 if (typeof line == "number") {
7844 if (!isLine(this, line)) { return null }
7845 n = line;
7846 line = getLine(this, line);
7847 if (!line) { return null }
7848 } else {
7849 n = lineNo(line);
7850 if (n == null) { return null }
7851 }
7852 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7853 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7854 widgets: line.widgets}
7855 },
7856
7857 addLineClass: docMethodOp(function(handle, where, cls) {
7858 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7859 var prop = where == "text" ? "textClass"
7860 : where == "background" ? "bgClass"
7861 : where == "gutter" ? "gutterClass" : "wrapClass";
7862 if (!line[prop]) { line[prop] = cls; }
7863 else if (classTest(cls).test(line[prop])) { return false }
7864 else { line[prop] += " " + cls; }
7865 return true
7866 })
7867 }),
7868 removeLineClass: docMethodOp(function(handle, where, cls) {
7869 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7870 var prop = where == "text" ? "textClass"
7871 : where == "background" ? "bgClass"
7872 : where == "gutter" ? "gutterClass" : "wrapClass";
7873 var cur = line[prop];
7874 if (!cur) { return false }
7875 else if (cls == null) { line[prop] = null; }
7876 else {
7877 var found = cur.match(classTest(cls));
7878 if (!found) { return false }
7879 var end = found.index + found[0].length;
7880 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7881 }
7882 return true
7883 })
7884 }),
7885
7886 addLineWidget: docMethodOp(function(handle, node, options) {
7887 return addLineWidget(this, handle, node, options)
7888 }),
7889 removeLineWidget: function(widget) { widget.clear(); },
7890
7891 markText: function(from, to, options) {
7892 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7893 },
7894 setBookmark: function(pos, options) {
7895 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7896 insertLeft: options && options.insertLeft,
7897 clearWhenEmpty: false, shared: options && options.shared,
7898 handleMouseEvents: options && options.handleMouseEvents};
7899 pos = clipPos(this, pos);
7900 return markText(this, pos, pos, realOpts, "bookmark")
7901 },
7902 findMarksAt: function(pos) {
7903 pos = clipPos(this, pos);
7904 var markers = [], spans = getLine(this, pos.line).markedSpans;
7905 if (spans) { for (var i = 0; i < spans.length; ++i) {
7906 var span = spans[i];
7907 if ((span.from == null || span.from <= pos.ch) &&
7908 (span.to == null || span.to >= pos.ch))
7909 { markers.push(span.marker.parent || span.marker); }
7910 } }
7911 return markers
7912 },
7913 findMarks: function(from, to, filter) {
7914 from = clipPos(this, from); to = clipPos(this, to);
7915 var found = [], lineNo$$1 = from.line;
7916 this.iter(from.line, to.line + 1, function (line) {
7917 var spans = line.markedSpans;
7918 if (spans) { for (var i = 0; i < spans.length; i++) {
7919 var span = spans[i];
7920 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7921 span.from == null && lineNo$$1 != from.line ||
7922 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7923 (!filter || filter(span.marker)))
7924 { found.push(span.marker.parent || span.marker); }
7925 } }
7926 ++lineNo$$1;
7927 });
7928 return found
7929 },
7930 getAllMarks: function() {
7931 var markers = [];
7932 this.iter(function (line) {
7933 var sps = line.markedSpans;
7934 if (sps) { for (var i = 0; i < sps.length; ++i)
7935 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7936 });
7937 return markers
7938 },
7939
7940 posFromIndex: function(off) {
7941 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7942 this.iter(function (line) {
7943 var sz = line.text.length + sepSize;
7944 if (sz > off) { ch = off; return true }
7945 off -= sz;
7946 ++lineNo$$1;
7947 });
7948 return clipPos(this, Pos(lineNo$$1, ch))
7949 },
7950 indexFromPos: function (coords) {
7951 coords = clipPos(this, coords);
7952 var index = coords.ch;
7953 if (coords.line < this.first || coords.ch < 0) { return 0 }
7954 var sepSize = this.lineSeparator().length;
7955 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
7956 index += line.text.length + sepSize;
7957 });
7958 return index
7959 },
7960
7961 copy: function(copyHistory) {
7962 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7963 this.modeOption, this.first, this.lineSep, this.direction);
7964 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7965 doc.sel = this.sel;
7966 doc.extend = false;
7967 if (copyHistory) {
7968 doc.history.undoDepth = this.history.undoDepth;
7969 doc.setHistory(this.getHistory());
7970 }
7971 return doc
7972 },
7973
7974 linkedDoc: function(options) {
7975 if (!options) { options = {}; }
7976 var from = this.first, to = this.first + this.size;
7977 if (options.from != null && options.from > from) { from = options.from; }
7978 if (options.to != null && options.to < to) { to = options.to; }
7979 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
7980 if (options.sharedHist) { copy.history = this.history
7981 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7982 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7983 copySharedMarkers(copy, findSharedMarkers(this));
7984 return copy
7985 },
7986 unlinkDoc: function(other) {
7987 var this$1 = this;
7988
7989 if (other instanceof CodeMirror$1) { other = other.doc; }
7990 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
7991 var link = this$1.linked[i];
7992 if (link.doc != other) { continue }
7993 this$1.linked.splice(i, 1);
7994 other.unlinkDoc(this$1);
7995 detachSharedMarkers(findSharedMarkers(this$1));
7996 break
7997 } }
7998 // If the histories were shared, split them again
7999 if (other.history == this.history) {
8000 var splitIds = [other.id];
8001 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
8002 other.history = new History(null);
8003 other.history.done = copyHistoryArray(this.history.done, splitIds);
8004 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
8005 }
8006 },
8007 iterLinkedDocs: function(f) {linkedDocs(this, f);},
8008
8009 getMode: function() {return this.mode},
8010 getEditor: function() {return this.cm},
8011
8012 splitLines: function(str) {
8013 if (this.lineSep) { return str.split(this.lineSep) }
8014 return splitLinesAuto(str)
8015 },
8016 lineSeparator: function() { return this.lineSep || "\n" },
8017
8018 setDirection: docMethodOp(function (dir) {
8019 if (dir != "rtl") { dir = "ltr"; }
8020 if (dir == this.direction) { return }
8021 this.direction = dir;
8022 this.iter(function (line) { return line.order = null; });
8023 if (this.cm) { directionChanged(this.cm); }
8024 })
8025 });
8026
8027 // Public alias.
8028 Doc.prototype.eachLine = Doc.prototype.iter;
8029
8030 // Kludge to work around strange IE behavior where it'll sometimes
8031 // re-fire a series of drag-related events right after the drop (#1551)
8032 var lastDrop = 0;
8033
8034 function onDrop(e) {
8035 var cm = this;
8036 clearDragCursor(cm);
8037 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
8038 { return }
8039 e_preventDefault(e);
8040 if (ie) { lastDrop = +new Date; }
8041 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
8042 if (!pos || cm.isReadOnly()) { return }
8043 // Might be a file drop, in which case we simply extract the text
8044 // and insert it.
8045 if (files && files.length && window.FileReader && window.File) {
8046 var n = files.length, text = Array(n), read = 0;
8047 var loadFile = function (file, i) {
8048 if (cm.options.allowDropFileTypes &&
8049 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
8050 { return }
8051
8052 var reader = new FileReader;
8053 reader.onload = operation(cm, function () {
8054 var content = reader.result;
8055 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
8056 text[i] = content;
8057 if (++read == n) {
8058 pos = clipPos(cm.doc, pos);
8059 var change = {from: pos, to: pos,
8060 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
8061 origin: "paste"};
8062 makeChange(cm.doc, change);
8063 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
8064 }
8065 });
8066 reader.readAsText(file);
8067 };
8068 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
8069 } else { // Normal drop
8070 // Don't do a replace if the drop happened inside of the selected text.
8071 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
8072 cm.state.draggingText(e);
8073 // Ensure the editor is re-focused
8074 setTimeout(function () { return cm.display.input.focus(); }, 20);
8075 return
8076 }
8077 try {
8078 var text$1 = e.dataTransfer.getData("Text");
8079 if (text$1) {
8080 var selected;
8081 if (cm.state.draggingText && !cm.state.draggingText.copy)
8082 { selected = cm.listSelections(); }
8083 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
8084 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
8085 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
8086 cm.replaceSelection(text$1, "around", "paste");
8087 cm.display.input.focus();
8088 }
8089 }
8090 catch(e){}
8091 }
8092 }
8093
8094 function onDragStart(cm, e) {
8095 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
8096 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
8097
8098 e.dataTransfer.setData("Text", cm.getSelection());
8099 e.dataTransfer.effectAllowed = "copyMove";
8100
8101 // Use dummy image instead of default browsers image.
8102 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
8103 if (e.dataTransfer.setDragImage && !safari) {
8104 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
8105 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
8106 if (presto) {
8107 img.width = img.height = 1;
8108 cm.display.wrapper.appendChild(img);
8109 // Force a relayout, or Opera won't use our image for some obscure reason
8110 img._top = img.offsetTop;
8111 }
8112 e.dataTransfer.setDragImage(img, 0, 0);
8113 if (presto) { img.parentNode.removeChild(img); }
8114 }
8115 }
8116
8117 function onDragOver(cm, e) {
8118 var pos = posFromMouse(cm, e);
8119 if (!pos) { return }
8120 var frag = document.createDocumentFragment();
8121 drawSelectionCursor(cm, pos, frag);
8122 if (!cm.display.dragCursor) {
8123 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8124 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8125 }
8126 removeChildrenAndAdd(cm.display.dragCursor, frag);
8127 }
8128
8129 function clearDragCursor(cm) {
8130 if (cm.display.dragCursor) {
8131 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8132 cm.display.dragCursor = null;
8133 }
8134 }
8135
8136 // These must be handled carefully, because naively registering a
8137 // handler for each editor will cause the editors to never be
8138 // garbage collected.
8139
8140 function forEachCodeMirror(f) {
8141 if (!document.getElementsByClassName) { return }
8142 var byClass = document.getElementsByClassName("CodeMirror");
8143 for (var i = 0; i < byClass.length; i++) {
8144 var cm = byClass[i].CodeMirror;
8145 if (cm) { f(cm); }
8146 }
8147 }
8148
8149 var globalsRegistered = false;
8150 function ensureGlobalHandlers() {
8151 if (globalsRegistered) { return }
8152 registerGlobalHandlers();
8153 globalsRegistered = true;
8154 }
8155 function registerGlobalHandlers() {
8156 // When the window resizes, we need to refresh active editors.
8157 var resizeTimer;
8158 on(window, "resize", function () {
8159 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8160 resizeTimer = null;
8161 forEachCodeMirror(onResize);
8162 }, 100); }
8163 });
8164 // When the window loses focus, we want to show the editor as blurred
8165 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8166 }
8167 // Called when the window resizes
8168 function onResize(cm) {
8169 var d = cm.display;
8170 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
8171 { return }
8172 // Might be a text scaling operation, clear size caches.
8173 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8174 d.scrollbarsClipped = false;
8175 cm.setSize();
8176 }
8177
8178 var keyNames = {
8179 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8180 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8181 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8182 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8183 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
8184 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8185 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8186 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8187 };
8188
8189 // Number keys
8190 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8191 // Alphabetic keys
8192 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8193 // Function keys
8194 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8195
8196 var keyMap = {};
8197
8198 keyMap.basic = {
8199 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8200 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8201 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8202 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8203 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8204 "Esc": "singleSelection"
8205 };
8206 // Note that the save and find-related commands aren't defined by
8207 // default. User code or addons can define them. Unknown commands
8208 // are simply ignored.
8209 keyMap.pcDefault = {
8210 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8211 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8212 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8213 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8214 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8215 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8216 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8217 fallthrough: "basic"
8218 };
8219 // Very basic readline/emacs-style bindings, which are standard on Mac.
8220 keyMap.emacsy = {
8221 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8222 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8223 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8224 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8225 "Ctrl-O": "openLine"
8226 };
8227 keyMap.macDefault = {
8228 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8229 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8230 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8231 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8232 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8233 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8234 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8235 fallthrough: ["basic", "emacsy"]
8236 };
8237 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8238
8239 // KEYMAP DISPATCH
8240
8241 function normalizeKeyName(name) {
8242 var parts = name.split(/-(?!$)/);
8243 name = parts[parts.length - 1];
8244 var alt, ctrl, shift, cmd;
8245 for (var i = 0; i < parts.length - 1; i++) {
8246 var mod = parts[i];
8247 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8248 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8249 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8250 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8251 else { throw new Error("Unrecognized modifier name: " + mod) }
8252 }
8253 if (alt) { name = "Alt-" + name; }
8254 if (ctrl) { name = "Ctrl-" + name; }
8255 if (cmd) { name = "Cmd-" + name; }
8256 if (shift) { name = "Shift-" + name; }
8257 return name
8258 }
8259
8260 // This is a kludge to keep keymaps mostly working as raw objects
8261 // (backwards compatibility) while at the same time support features
8262 // like normalization and multi-stroke key bindings. It compiles a
8263 // new normalized keymap, and then updates the old object to reflect
8264 // this.
8265 function normalizeKeyMap(keymap) {
8266 var copy = {};
8267 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8268 var value = keymap[keyname];
8269 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8270 if (value == "...") { delete keymap[keyname]; continue }
8271
8272 var keys = map(keyname.split(" "), normalizeKeyName);
8273 for (var i = 0; i < keys.length; i++) {
8274 var val = (void 0), name = (void 0);
8275 if (i == keys.length - 1) {
8276 name = keys.join(" ");
8277 val = value;
8278 } else {
8279 name = keys.slice(0, i + 1).join(" ");
8280 val = "...";
8281 }
8282 var prev = copy[name];
8283 if (!prev) { copy[name] = val; }
8284 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8285 }
8286 delete keymap[keyname];
8287 } }
8288 for (var prop in copy) { keymap[prop] = copy[prop]; }
8289 return keymap
8290 }
8291
8292 function lookupKey(key, map$$1, handle, context) {
8293 map$$1 = getKeyMap(map$$1);
8294 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8295 if (found === false) { return "nothing" }
8296 if (found === "...") { return "multi" }
8297 if (found != null && handle(found)) { return "handled" }
8298
8299 if (map$$1.fallthrough) {
8300 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8301 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8302 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8303 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8304 if (result) { return result }
8305 }
8306 }
8307 }
8308
8309 // Modifier key presses don't count as 'real' key presses for the
8310 // purpose of keymap fallthrough.
8311 function isModifierKey(value) {
8312 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8313 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8314 }
8315
8316 function addModifierNames(name, event, noShift) {
8317 var base = name;
8318 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8319 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8320 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8321 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8322 return name
8323 }
8324
8325 // Look up the name of a key as indicated by an event object.
8326 function keyName(event, noShift) {
8327 if (presto && event.keyCode == 34 && event["char"]) { return false }
8328 var name = keyNames[event.keyCode];
8329 if (name == null || event.altGraphKey) { return false }
8330 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8331 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8332 if (event.keyCode == 3 && event.code) { name = event.code; }
8333 return addModifierNames(name, event, noShift)
8334 }
8335
8336 function getKeyMap(val) {
8337 return typeof val == "string" ? keyMap[val] : val
8338 }
8339
8340 // Helper for deleting text near the selection(s), used to implement
8341 // backspace, delete, and similar functionality.
8342 function deleteNearSelection(cm, compute) {
8343 var ranges = cm.doc.sel.ranges, kill = [];
8344 // Build up a set of ranges to kill first, merging overlapping
8345 // ranges.
8346 for (var i = 0; i < ranges.length; i++) {
8347 var toKill = compute(ranges[i]);
8348 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8349 var replaced = kill.pop();
8350 if (cmp(replaced.from, toKill.from) < 0) {
8351 toKill.from = replaced.from;
8352 break
8353 }
8354 }
8355 kill.push(toKill);
8356 }
8357 // Next, remove those actual ranges.
8358 runInOp(cm, function () {
8359 for (var i = kill.length - 1; i >= 0; i--)
8360 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8361 ensureCursorVisible(cm);
8362 });
8363 }
8364
8365 function moveCharLogically(line, ch, dir) {
8366 var target = skipExtendingChars(line.text, ch + dir, dir);
8367 return target < 0 || target > line.text.length ? null : target
8368 }
8369
8370 function moveLogically(line, start, dir) {
8371 var ch = moveCharLogically(line, start.ch, dir);
8372 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8373 }
8374
8375 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8376 if (visually) {
8377 var order = getOrder(lineObj, cm.doc.direction);
8378 if (order) {
8379 var part = dir < 0 ? lst(order) : order[0];
8380 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8381 var sticky = moveInStorageOrder ? "after" : "before";
8382 var ch;
8383 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8384 // it could be that the last bidi part is not on the last visual line,
8385 // since visual lines contain content order-consecutive chunks.
8386 // Thus, in rtl, we are looking for the first (content-order) character
8387 // in the rtl chunk that is on the last line (that is, the same line
8388 // as the last (content-order) character).
8389 if (part.level > 0 || cm.doc.direction == "rtl") {
8390 var prep = prepareMeasureForLine(cm, lineObj);
8391 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8392 var targetTop = measureCharPrepared(cm, prep, ch).top;
8393 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8394 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8395 } else { ch = dir < 0 ? part.to : part.from; }
8396 return new Pos(lineNo, ch, sticky)
8397 }
8398 }
8399 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8400 }
8401
8402 function moveVisually(cm, line, start, dir) {
8403 var bidi = getOrder(line, cm.doc.direction);
8404 if (!bidi) { return moveLogically(line, start, dir) }
8405 if (start.ch >= line.text.length) {
8406 start.ch = line.text.length;
8407 start.sticky = "before";
8408 } else if (start.ch <= 0) {
8409 start.ch = 0;
8410 start.sticky = "after";
8411 }
8412 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8413 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8414 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8415 // nothing interesting happens.
8416 return moveLogically(line, start, dir)
8417 }
8418
8419 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8420 var prep;
8421 var getWrappedLineExtent = function (ch) {
8422 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8423 prep = prep || prepareMeasureForLine(cm, line);
8424 return wrappedLineExtentChar(cm, line, prep, ch)
8425 };
8426 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8427
8428 if (cm.doc.direction == "rtl" || part.level == 1) {
8429 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8430 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8431 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8432 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8433 var sticky = moveInStorageOrder ? "before" : "after";
8434 return new Pos(start.line, ch, sticky)
8435 }
8436 }
8437
8438 // Case 3: Could not move within this bidi part in this visual line, so leave
8439 // the current bidi part
8440
8441 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8442 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8443 ? new Pos(start.line, mv(ch, 1), "before")
8444 : new Pos(start.line, ch, "after"); };
8445
8446 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8447 var part = bidi[partPos];
8448 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8449 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8450 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8451 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8452 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8453 }
8454 };
8455
8456 // Case 3a: Look for other bidi parts on the same visual line
8457 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8458 if (res) { return res }
8459
8460 // Case 3b: Look for other bidi parts on the next visual line
8461 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8462 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8463 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8464 if (res) { return res }
8465 }
8466
8467 // Case 4: Nowhere to move
8468 return null
8469 }
8470
8471 // Commands are parameter-less actions that can be performed on an
8472 // editor, mostly used for keybindings.
8473 var commands = {
8474 selectAll: selectAll,
8475 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8476 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8477 if (range.empty()) {
8478 var len = getLine(cm.doc, range.head.line).text.length;
8479 if (range.head.ch == len && range.head.line < cm.lastLine())
8480 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8481 else
8482 { return {from: range.head, to: Pos(range.head.line, len)} }
8483 } else {
8484 return {from: range.from(), to: range.to()}
8485 }
8486 }); },
8487 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8488 from: Pos(range.from().line, 0),
8489 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8490 }); }); },
8491 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8492 from: Pos(range.from().line, 0), to: range.from()
8493 }); }); },
8494 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8495 var top = cm.charCoords(range.head, "div").top + 5;
8496 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8497 return {from: leftPos, to: range.from()}
8498 }); },
8499 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8500 var top = cm.charCoords(range.head, "div").top + 5;
8501 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8502 return {from: range.from(), to: rightPos }
8503 }); },
8504 undo: function (cm) { return cm.undo(); },
8505 redo: function (cm) { return cm.redo(); },
8506 undoSelection: function (cm) { return cm.undoSelection(); },
8507 redoSelection: function (cm) { return cm.redoSelection(); },
8508 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8509 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8510 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8511 {origin: "+move", bias: 1}
8512 ); },
8513 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8514 {origin: "+move", bias: 1}
8515 ); },
8516 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8517 {origin: "+move", bias: -1}
8518 ); },
8519 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8520 var top = cm.cursorCoords(range.head, "div").top + 5;
8521 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8522 }, sel_move); },
8523 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8524 var top = cm.cursorCoords(range.head, "div").top + 5;
8525 return cm.coordsChar({left: 0, top: top}, "div")
8526 }, sel_move); },
8527 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8528 var top = cm.cursorCoords(range.head, "div").top + 5;
8529 var pos = cm.coordsChar({left: 0, top: top}, "div");
8530 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8531 return pos
8532 }, sel_move); },
8533 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8534 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8535 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8536 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8537 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8538 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8539 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8540 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8541 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8542 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8543 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8544 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8545 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8546 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8547 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8548 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8549 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8550 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8551 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8552 indentMore: function (cm) { return cm.indentSelection("add"); },
8553 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8554 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8555 insertSoftTab: function (cm) {
8556 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8557 for (var i = 0; i < ranges.length; i++) {
8558 var pos = ranges[i].from();
8559 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8560 spaces.push(spaceStr(tabSize - col % tabSize));
8561 }
8562 cm.replaceSelections(spaces);
8563 },
8564 defaultTab: function (cm) {
8565 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8566 else { cm.execCommand("insertTab"); }
8567 },
8568 // Swap the two chars left and right of each selection's head.
8569 // Move cursor behind the two swapped characters afterwards.
8570 //
8571 // Doesn't consider line feeds a character.
8572 // Doesn't scan more than one line above to find a character.
8573 // Doesn't do anything on an empty line.
8574 // Doesn't do anything with non-empty selections.
8575 transposeChars: function (cm) { return runInOp(cm, function () {
8576 var ranges = cm.listSelections(), newSel = [];
8577 for (var i = 0; i < ranges.length; i++) {
8578 if (!ranges[i].empty()) { continue }
8579 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8580 if (line) {
8581 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8582 if (cur.ch > 0) {
8583 cur = new Pos(cur.line, cur.ch + 1);
8584 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8585 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8586 } else if (cur.line > cm.doc.first) {
8587 var prev = getLine(cm.doc, cur.line - 1).text;
8588 if (prev) {
8589 cur = new Pos(cur.line, 1);
8590 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8591 prev.charAt(prev.length - 1),
8592 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8593 }
8594 }
8595 }
8596 newSel.push(new Range(cur, cur));
8597 }
8598 cm.setSelections(newSel);
8599 }); },
8600 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8601 var sels = cm.listSelections();
8602 for (var i = sels.length - 1; i >= 0; i--)
8603 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8604 sels = cm.listSelections();
8605 for (var i$1 = 0; i$1 < sels.length; i$1++)
8606 { cm.indentLine(sels[i$1].from().line, null, true); }
8607 ensureCursorVisible(cm);
8608 }); },
8609 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8610 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8611 };
8612
8613
8614 function lineStart(cm, lineN) {
8615 var line = getLine(cm.doc, lineN);
8616 var visual = visualLine(line);
8617 if (visual != line) { lineN = lineNo(visual); }
8618 return endOfLine(true, cm, visual, lineN, 1)
8619 }
8620 function lineEnd(cm, lineN) {
8621 var line = getLine(cm.doc, lineN);
8622 var visual = visualLineEnd(line);
8623 if (visual != line) { lineN = lineNo(visual); }
8624 return endOfLine(true, cm, line, lineN, -1)
8625 }
8626 function lineStartSmart(cm, pos) {
8627 var start = lineStart(cm, pos.line);
8628 var line = getLine(cm.doc, start.line);
8629 var order = getOrder(line, cm.doc.direction);
8630 if (!order || order[0].level == 0) {
8631 var firstNonWS = Math.max(0, line.text.search(/\S/));
8632 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8633 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8634 }
8635 return start
8636 }
8637
8638 // Run a handler that was bound to a key.
8639 function doHandleBinding(cm, bound, dropShift) {
8640 if (typeof bound == "string") {
8641 bound = commands[bound];
8642 if (!bound) { return false }
8643 }
8644 // Ensure previous input has been read, so that the handler sees a
8645 // consistent view of the document
8646 cm.display.input.ensurePolled();
8647 var prevShift = cm.display.shift, done = false;
8648 try {
8649 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8650 if (dropShift) { cm.display.shift = false; }
8651 done = bound(cm) != Pass;
8652 } finally {
8653 cm.display.shift = prevShift;
8654 cm.state.suppressEdits = false;
8655 }
8656 return done
8657 }
8658
8659 function lookupKeyForEditor(cm, name, handle) {
8660 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8661 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8662 if (result) { return result }
8663 }
8664 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8665 || lookupKey(name, cm.options.keyMap, handle, cm)
8666 }
8667
8668 // Note that, despite the name, this function is also used to check
8669 // for bound mouse clicks.
8670
8671 var stopSeq = new Delayed;
8672
8673 function dispatchKey(cm, name, e, handle) {
8674 var seq = cm.state.keySeq;
8675 if (seq) {
8676 if (isModifierKey(name)) { return "handled" }
8677 if (/\'$/.test(name))
8678 { cm.state.keySeq = null; }
8679 else
8680 { stopSeq.set(50, function () {
8681 if (cm.state.keySeq == seq) {
8682 cm.state.keySeq = null;
8683 cm.display.input.reset();
8684 }
8685 }); }
8686 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8687 }
8688 return dispatchKeyInner(cm, name, e, handle)
8689 }
8690
8691 function dispatchKeyInner(cm, name, e, handle) {
8692 var result = lookupKeyForEditor(cm, name, handle);
8693
8694 if (result == "multi")
8695 { cm.state.keySeq = name; }
8696 if (result == "handled")
8697 { signalLater(cm, "keyHandled", cm, name, e); }
8698
8699 if (result == "handled" || result == "multi") {
8700 e_preventDefault(e);
8701 restartBlink(cm);
8702 }
8703
8704 return !!result
8705 }
8706
8707 // Handle a key from the keydown event.
8708 function handleKeyBinding(cm, e) {
8709 var name = keyName(e, true);
8710 if (!name) { return false }
8711
8712 if (e.shiftKey && !cm.state.keySeq) {
8713 // First try to resolve full name (including 'Shift-'). Failing
8714 // that, see if there is a cursor-motion command (starting with
8715 // 'go') bound to the keyname without 'Shift-'.
8716 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8717 || dispatchKey(cm, name, e, function (b) {
8718 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8719 { return doHandleBinding(cm, b) }
8720 })
8721 } else {
8722 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8723 }
8724 }
8725
8726 // Handle a key from the keypress event
8727 function handleCharBinding(cm, e, ch) {
8728 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8729 }
8730
8731 var lastStoppedKey = null;
8732 function onKeyDown(e) {
8733 var cm = this;
8734 cm.curOp.focus = activeElt();
8735 if (signalDOMEvent(cm, e)) { return }
8736 // IE does strange things with escape.
8737 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8738 var code = e.keyCode;
8739 cm.display.shift = code == 16 || e.shiftKey;
8740 var handled = handleKeyBinding(cm, e);
8741 if (presto) {
8742 lastStoppedKey = handled ? code : null;
8743 // Opera has no cut event... we try to at least catch the key combo
8744 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8745 { cm.replaceSelection("", null, "cut"); }
8746 }
8747
8748 // Turn mouse into crosshair when Alt is held on Mac.
8749 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8750 { showCrossHair(cm); }
8751 }
8752
8753 function showCrossHair(cm) {
8754 var lineDiv = cm.display.lineDiv;
8755 addClass(lineDiv, "CodeMirror-crosshair");
8756
8757 function up(e) {
8758 if (e.keyCode == 18 || !e.altKey) {
8759 rmClass(lineDiv, "CodeMirror-crosshair");
8760 off(document, "keyup", up);
8761 off(document, "mouseover", up);
8762 }
8763 }
8764 on(document, "keyup", up);
8765 on(document, "mouseover", up);
8766 }
8767
8768 function onKeyUp(e) {
8769 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8770 signalDOMEvent(this, e);
8771 }
8772
8773 function onKeyPress(e) {
8774 var cm = this;
8775 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8776 var keyCode = e.keyCode, charCode = e.charCode;
8777 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8778 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8779 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8780 // Some browsers fire keypress events for backspace
8781 if (ch == "\x08") { return }
8782 if (handleCharBinding(cm, e, ch)) { return }
8783 cm.display.input.onKeyPress(e);
8784 }
8785
8786 var DOUBLECLICK_DELAY = 400;
8787
8788 var PastClick = function(time, pos, button) {
8789 this.time = time;
8790 this.pos = pos;
8791 this.button = button;
8792 };
8793
8794 PastClick.prototype.compare = function (time, pos, button) {
8795 return this.time + DOUBLECLICK_DELAY > time &&
8796 cmp(pos, this.pos) == 0 && button == this.button
8797 };
8798
8799 var lastClick;
8800 var lastDoubleClick;
8801 function clickRepeat(pos, button) {
8802 var now = +new Date;
8803 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8804 lastClick = lastDoubleClick = null;
8805 return "triple"
8806 } else if (lastClick && lastClick.compare(now, pos, button)) {
8807 lastDoubleClick = new PastClick(now, pos, button);
8808 lastClick = null;
8809 return "double"
8810 } else {
8811 lastClick = new PastClick(now, pos, button);
8812 lastDoubleClick = null;
8813 return "single"
8814 }
8815 }
8816
8817 // A mouse down can be a single click, double click, triple click,
8818 // start of selection drag, start of text drag, new cursor
8819 // (ctrl-click), rectangle drag (alt-drag), or xwin
8820 // middle-click-paste. Or it might be a click on something we should
8821 // not interfere with, such as a scrollbar or widget.
8822 function onMouseDown(e) {
8823 var cm = this, display = cm.display;
8824 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8825 display.input.ensurePolled();
8826 display.shift = e.shiftKey;
8827
8828 if (eventInWidget(display, e)) {
8829 if (!webkit) {
8830 // Briefly turn off draggability, to allow widgets to do
8831 // normal dragging things.
8832 display.scroller.draggable = false;
8833 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8834 }
8835 return
8836 }
8837 if (clickInGutter(cm, e)) { return }
8838 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8839 window.focus();
8840
8841 // #3261: make sure, that we're not starting a second selection
8842 if (button == 1 && cm.state.selectingText)
8843 { cm.state.selectingText(e); }
8844
8845 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8846
8847 if (button == 1) {
8848 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8849 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8850 } else if (button == 2) {
8851 if (pos) { extendSelection(cm.doc, pos); }
8852 setTimeout(function () { return display.input.focus(); }, 20);
8853 } else if (button == 3) {
8854 if (captureRightClick) { onContextMenu(cm, e); }
8855 else { delayBlurEvent(cm); }
8856 }
8857 }
8858
8859 function handleMappedButton(cm, button, pos, repeat, event) {
8860 var name = "Click";
8861 if (repeat == "double") { name = "Double" + name; }
8862 else if (repeat == "triple") { name = "Triple" + name; }
8863 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8864
8865 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8866 if (typeof bound == "string") { bound = commands[bound]; }
8867 if (!bound) { return false }
8868 var done = false;
8869 try {
8870 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8871 done = bound(cm, pos) != Pass;
8872 } finally {
8873 cm.state.suppressEdits = false;
8874 }
8875 return done
8876 })
8877 }
8878
8879 function configureMouse(cm, repeat, event) {
8880 var option = cm.getOption("configureMouse");
8881 var value = option ? option(cm, repeat, event) : {};
8882 if (value.unit == null) {
8883 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8884 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8885 }
8886 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8887 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8888 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8889 return value
8890 }
8891
8892 function leftButtonDown(cm, pos, repeat, event) {
8893 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8894 else { cm.curOp.focus = activeElt(); }
8895
8896 var behavior = configureMouse(cm, repeat, event);
8897
8898 var sel = cm.doc.sel, contained;
8899 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8900 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8901 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8902 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8903 { leftButtonStartDrag(cm, event, pos, behavior); }
8904 else
8905 { leftButtonSelect(cm, event, pos, behavior); }
8906 }
8907
8908 // Start a text drag. When it ends, see if any dragging actually
8909 // happen, and treat as a click if it didn't.
8910 function leftButtonStartDrag(cm, event, pos, behavior) {
8911 var display = cm.display, moved = false;
8912 var dragEnd = operation(cm, function (e) {
8913 if (webkit) { display.scroller.draggable = false; }
8914 cm.state.draggingText = false;
8915 off(document, "mouseup", dragEnd);
8916 off(document, "mousemove", mouseMove);
8917 off(display.scroller, "dragstart", dragStart);
8918 off(display.scroller, "drop", dragEnd);
8919 if (!moved) {
8920 e_preventDefault(e);
8921 if (!behavior.addNew)
8922 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8923 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8924 if (webkit || ie && ie_version == 9)
8925 { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); }
8926 else
8927 { display.input.focus(); }
8928 }
8929 });
8930 var mouseMove = function(e2) {
8931 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8932 };
8933 var dragStart = function () { return moved = true; };
8934 // Let the drag handler handle this.
8935 if (webkit) { display.scroller.draggable = true; }
8936 cm.state.draggingText = dragEnd;
8937 dragEnd.copy = !behavior.moveOnDrag;
8938 // IE's approach to draggable
8939 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8940 on(document, "mouseup", dragEnd);
8941 on(document, "mousemove", mouseMove);
8942 on(display.scroller, "dragstart", dragStart);
8943 on(display.scroller, "drop", dragEnd);
8944
8945 delayBlurEvent(cm);
8946 setTimeout(function () { return display.input.focus(); }, 20);
8947 }
8948
8949 function rangeForUnit(cm, pos, unit) {
8950 if (unit == "char") { return new Range(pos, pos) }
8951 if (unit == "word") { return cm.findWordAt(pos) }
8952 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
8953 var result = unit(cm, pos);
8954 return new Range(result.from, result.to)
8955 }
8956
8957 // Normal selection, as opposed to text dragging.
8958 function leftButtonSelect(cm, event, start, behavior) {
8959 var display = cm.display, doc = cm.doc;
8960 e_preventDefault(event);
8961
8962 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
8963 if (behavior.addNew && !behavior.extend) {
8964 ourIndex = doc.sel.contains(start);
8965 if (ourIndex > -1)
8966 { ourRange = ranges[ourIndex]; }
8967 else
8968 { ourRange = new Range(start, start); }
8969 } else {
8970 ourRange = doc.sel.primary();
8971 ourIndex = doc.sel.primIndex;
8972 }
8973
8974 if (behavior.unit == "rectangle") {
8975 if (!behavior.addNew) { ourRange = new Range(start, start); }
8976 start = posFromMouse(cm, event, true, true);
8977 ourIndex = -1;
8978 } else {
8979 var range$$1 = rangeForUnit(cm, start, behavior.unit);
8980 if (behavior.extend)
8981 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
8982 else
8983 { ourRange = range$$1; }
8984 }
8985
8986 if (!behavior.addNew) {
8987 ourIndex = 0;
8988 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
8989 startSel = doc.sel;
8990 } else if (ourIndex == -1) {
8991 ourIndex = ranges.length;
8992 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
8993 {scroll: false, origin: "*mouse"});
8994 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
8995 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
8996 {scroll: false, origin: "*mouse"});
8997 startSel = doc.sel;
8998 } else {
8999 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
9000 }
9001
9002 var lastPos = start;
9003 function extendTo(pos) {
9004 if (cmp(lastPos, pos) == 0) { return }
9005 lastPos = pos;
9006
9007 if (behavior.unit == "rectangle") {
9008 var ranges = [], tabSize = cm.options.tabSize;
9009 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
9010 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
9011 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
9012 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
9013 line <= end; line++) {
9014 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
9015 if (left == right)
9016 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
9017 else if (text.length > leftPos)
9018 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
9019 }
9020 if (!ranges.length) { ranges.push(new Range(start, start)); }
9021 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
9022 {origin: "*mouse", scroll: false});
9023 cm.scrollIntoView(pos);
9024 } else {
9025 var oldRange = ourRange;
9026 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
9027 var anchor = oldRange.anchor, head;
9028 if (cmp(range$$1.anchor, anchor) > 0) {
9029 head = range$$1.head;
9030 anchor = minPos(oldRange.from(), range$$1.anchor);
9031 } else {
9032 head = range$$1.anchor;
9033 anchor = maxPos(oldRange.to(), range$$1.head);
9034 }
9035 var ranges$1 = startSel.ranges.slice(0);
9036 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
9037 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse);
9038 }
9039 }
9040
9041 var editorSize = display.wrapper.getBoundingClientRect();
9042 // Used to ensure timeout re-tries don't fire when another extend
9043 // happened in the meantime (clearTimeout isn't reliable -- at
9044 // least on Chrome, the timeouts still happen even when cleared,
9045 // if the clear happens after their scheduled firing time).
9046 var counter = 0;
9047
9048 function extend(e) {
9049 var curCount = ++counter;
9050 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
9051 if (!cur) { return }
9052 if (cmp(cur, lastPos) != 0) {
9053 cm.curOp.focus = activeElt();
9054 extendTo(cur);
9055 var visible = visibleLines(display, doc);
9056 if (cur.line >= visible.to || cur.line < visible.from)
9057 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
9058 } else {
9059 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
9060 if (outside) { setTimeout(operation(cm, function () {
9061 if (counter != curCount) { return }
9062 display.scroller.scrollTop += outside;
9063 extend(e);
9064 }), 50); }
9065 }
9066 }
9067
9068 function done(e) {
9069 cm.state.selectingText = false;
9070 counter = Infinity;
9071 e_preventDefault(e);
9072 display.input.focus();
9073 off(document, "mousemove", move);
9074 off(document, "mouseup", up);
9075 doc.history.lastSelOrigin = null;
9076 }
9077
9078 var move = operation(cm, function (e) {
9079 if (!e_button(e)) { done(e); }
9080 else { extend(e); }
9081 });
9082 var up = operation(cm, done);
9083 cm.state.selectingText = up;
9084 on(document, "mousemove", move);
9085 on(document, "mouseup", up);
9086 }
9087
9088 // Used when mouse-selecting to adjust the anchor to the proper side
9089 // of a bidi jump depending on the visual position of the head.
9090 function bidiSimplify(cm, range$$1) {
9091 var anchor = range$$1.anchor;
9092 var head = range$$1.head;
9093 var anchorLine = getLine(cm.doc, anchor.line);
9094 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
9095 var order = getOrder(anchorLine);
9096 if (!order) { return range$$1 }
9097 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
9098 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
9099 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
9100 if (boundary == 0 || boundary == order.length) { return range$$1 }
9101
9102 // Compute the relative visual position of the head compared to the
9103 // anchor (<0 is to the left, >0 to the right)
9104 var leftSide;
9105 if (head.line != anchor.line) {
9106 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
9107 } else {
9108 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
9109 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
9110 if (headIndex == boundary - 1 || headIndex == boundary)
9111 { leftSide = dir < 0; }
9112 else
9113 { leftSide = dir > 0; }
9114 }
9115
9116 var usePart = order[boundary + (leftSide ? -1 : 0)];
9117 var from = leftSide == (usePart.level == 1);
9118 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
9119 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
9120 }
9121
9122
9123 // Determines whether an event happened in the gutter, and fires the
9124 // handlers for the corresponding event.
9125 function gutterEvent(cm, e, type, prevent) {
9126 var mX, mY;
9127 if (e.touches) {
9128 mX = e.touches[0].clientX;
9129 mY = e.touches[0].clientY;
9130 } else {
9131 try { mX = e.clientX; mY = e.clientY; }
9132 catch(e) { return false }
9133 }
9134 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9135 if (prevent) { e_preventDefault(e); }
9136
9137 var display = cm.display;
9138 var lineBox = display.lineDiv.getBoundingClientRect();
9139
9140 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9141 mY -= lineBox.top - display.viewOffset;
9142
9143 for (var i = 0; i < cm.options.gutters.length; ++i) {
9144 var g = display.gutters.childNodes[i];
9145 if (g && g.getBoundingClientRect().right >= mX) {
9146 var line = lineAtHeight(cm.doc, mY);
9147 var gutter = cm.options.gutters[i];
9148 signal(cm, type, cm, line, gutter, e);
9149 return e_defaultPrevented(e)
9150 }
9151 }
9152 }
9153
9154 function clickInGutter(cm, e) {
9155 return gutterEvent(cm, e, "gutterClick", true)
9156 }
9157
9158 // CONTEXT MENU HANDLING
9159
9160 // To make the context menu work, we need to briefly unhide the
9161 // textarea (making it as unobtrusive as possible) to let the
9162 // right-click take effect on it.
9163 function onContextMenu(cm, e) {
9164 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9165 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9166 cm.display.input.onContextMenu(e);
9167 }
9168
9169 function contextMenuInGutter(cm, e) {
9170 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9171 return gutterEvent(cm, e, "gutterContextMenu", false)
9172 }
9173
9174 function themeChanged(cm) {
9175 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9176 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9177 clearCaches(cm);
9178 }
9179
9180 var Init = {toString: function(){return "CodeMirror.Init"}};
9181
9182 var defaults = {};
9183 var optionHandlers = {};
9184
9185 function defineOptions(CodeMirror) {
9186 var optionHandlers = CodeMirror.optionHandlers;
9187
9188 function option(name, deflt, handle, notOnInit) {
9189 CodeMirror.defaults[name] = deflt;
9190 if (handle) { optionHandlers[name] =
9191 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9192 }
9193
9194 CodeMirror.defineOption = option;
9195
9196 // Passed to option handlers when there is no old value.
9197 CodeMirror.Init = Init;
9198
9199 // These two are, on init, called from the constructor because they
9200 // have to be initialized before the editor can start at all.
9201 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9202 option("mode", null, function (cm, val) {
9203 cm.doc.modeOption = val;
9204 loadMode(cm);
9205 }, true);
9206
9207 option("indentUnit", 2, loadMode, true);
9208 option("indentWithTabs", false);
9209 option("smartIndent", true);
9210 option("tabSize", 4, function (cm) {
9211 resetModeState(cm);
9212 clearCaches(cm);
9213 regChange(cm);
9214 }, true);
9215
9216 option("lineSeparator", null, function (cm, val) {
9217 cm.doc.lineSep = val;
9218 if (!val) { return }
9219 var newBreaks = [], lineNo = cm.doc.first;
9220 cm.doc.iter(function (line) {
9221 for (var pos = 0;;) {
9222 var found = line.text.indexOf(val, pos);
9223 if (found == -1) { break }
9224 pos = found + val.length;
9225 newBreaks.push(Pos(lineNo, found));
9226 }
9227 lineNo++;
9228 });
9229 for (var i = newBreaks.length - 1; i >= 0; i--)
9230 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9231 });
9232 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
9233 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9234 if (old != Init) { cm.refresh(); }
9235 });
9236 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9237 option("electricChars", true);
9238 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9239 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9240 }, true);
9241 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9242 option("rtlMoveVisually", !windows);
9243 option("wholeLineUpdateBefore", true);
9244
9245 option("theme", "default", function (cm) {
9246 themeChanged(cm);
9247 guttersChanged(cm);
9248 }, true);
9249 option("keyMap", "default", function (cm, val, old) {
9250 var next = getKeyMap(val);
9251 var prev = old != Init && getKeyMap(old);
9252 if (prev && prev.detach) { prev.detach(cm, next); }
9253 if (next.attach) { next.attach(cm, prev || null); }
9254 });
9255 option("extraKeys", null);
9256 option("configureMouse", null);
9257
9258 option("lineWrapping", false, wrappingChanged, true);
9259 option("gutters", [], function (cm) {
9260 setGuttersForLineNumbers(cm.options);
9261 guttersChanged(cm);
9262 }, true);
9263 option("fixedGutter", true, function (cm, val) {
9264 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9265 cm.refresh();
9266 }, true);
9267 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9268 option("scrollbarStyle", "native", function (cm) {
9269 initScrollbars(cm);
9270 updateScrollbars(cm);
9271 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9272 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9273 }, true);
9274 option("lineNumbers", false, function (cm) {
9275 setGuttersForLineNumbers(cm.options);
9276 guttersChanged(cm);
9277 }, true);
9278 option("firstLineNumber", 1, guttersChanged, true);
9279 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
9280 option("showCursorWhenSelecting", false, updateSelection, true);
9281
9282 option("resetSelectionOnContextMenu", true);
9283 option("lineWiseCopyCut", true);
9284 option("pasteLinesPerSelection", true);
9285
9286 option("readOnly", false, function (cm, val) {
9287 if (val == "nocursor") {
9288 onBlur(cm);
9289 cm.display.input.blur();
9290 }
9291 cm.display.input.readOnlyChanged(val);
9292 });
9293 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9294 option("dragDrop", true, dragDropChanged);
9295 option("allowDropFileTypes", null);
9296
9297 option("cursorBlinkRate", 530);
9298 option("cursorScrollMargin", 0);
9299 option("cursorHeight", 1, updateSelection, true);
9300 option("singleCursorHeightPerLine", true, updateSelection, true);
9301 option("workTime", 100);
9302 option("workDelay", 100);
9303 option("flattenSpans", true, resetModeState, true);
9304 option("addModeClass", false, resetModeState, true);
9305 option("pollInterval", 100);
9306 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9307 option("historyEventDelay", 1250);
9308 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9309 option("maxHighlightLength", 10000, resetModeState, true);
9310 option("moveInputWithCursor", true, function (cm, val) {
9311 if (!val) { cm.display.input.resetPosition(); }
9312 });
9313
9314 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9315 option("autofocus", null);
9316 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9317 }
9318
9319 function guttersChanged(cm) {
9320 updateGutters(cm);
9321 regChange(cm);
9322 alignHorizontally(cm);
9323 }
9324
9325 function dragDropChanged(cm, value, old) {
9326 var wasOn = old && old != Init;
9327 if (!value != !wasOn) {
9328 var funcs = cm.display.dragFunctions;
9329 var toggle = value ? on : off;
9330 toggle(cm.display.scroller, "dragstart", funcs.start);
9331 toggle(cm.display.scroller, "dragenter", funcs.enter);
9332 toggle(cm.display.scroller, "dragover", funcs.over);
9333 toggle(cm.display.scroller, "dragleave", funcs.leave);
9334 toggle(cm.display.scroller, "drop", funcs.drop);
9335 }
9336 }
9337
9338 function wrappingChanged(cm) {
9339 if (cm.options.lineWrapping) {
9340 addClass(cm.display.wrapper, "CodeMirror-wrap");
9341 cm.display.sizer.style.minWidth = "";
9342 cm.display.sizerWidth = null;
9343 } else {
9344 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9345 findMaxLine(cm);
9346 }
9347 estimateLineHeights(cm);
9348 regChange(cm);
9349 clearCaches(cm);
9350 setTimeout(function () { return updateScrollbars(cm); }, 100);
9351 }
9352
9353 // A CodeMirror instance represents an editor. This is the object
9354 // that user code is usually dealing with.
9355
9356 function CodeMirror$1(place, options) {
9357 var this$1 = this;
9358
9359 if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
9360
9361 this.options = options = options ? copyObj(options) : {};
9362 // Determine effective options based on given values and defaults.
9363 copyObj(defaults, options, false);
9364 setGuttersForLineNumbers(options);
9365
9366 var doc = options.value;
9367 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9368 this.doc = doc;
9369
9370 var input = new CodeMirror$1.inputStyles[options.inputStyle](this);
9371 var display = this.display = new Display(place, doc, input);
9372 display.wrapper.CodeMirror = this;
9373 updateGutters(this);
9374 themeChanged(this);
9375 if (options.lineWrapping)
9376 { this.display.wrapper.className += " CodeMirror-wrap"; }
9377 initScrollbars(this);
9378
9379 this.state = {
9380 keyMaps: [], // stores maps added by addKeyMap
9381 overlays: [], // highlighting overlays, as added by addOverlay
9382 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9383 overwrite: false,
9384 delayingBlurEvent: false,
9385 focused: false,
9386 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9387 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
9388 selectingText: false,
9389 draggingText: false,
9390 highlight: new Delayed(), // stores highlight worker timeout
9391 keySeq: null, // Unfinished key sequence
9392 specialChars: null
9393 };
9394
9395 if (options.autofocus && !mobile) { display.input.focus(); }
9396
9397 // Override magic textarea content restore that IE sometimes does
9398 // on our hidden textarea on reload
9399 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9400
9401 registerEventHandlers(this);
9402 ensureGlobalHandlers();
9403
9404 startOperation(this);
9405 this.curOp.forceUpdate = true;
9406 attachDoc(this, doc);
9407
9408 if ((options.autofocus && !mobile) || this.hasFocus())
9409 { setTimeout(bind(onFocus, this), 20); }
9410 else
9411 { onBlur(this); }
9412
9413 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9414 { optionHandlers[opt](this$1, options[opt], Init); } }
9415 maybeUpdateLineNumberWidth(this);
9416 if (options.finishInit) { options.finishInit(this); }
9417 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9418 endOperation(this);
9419 // Suppress optimizelegibility in Webkit, since it breaks text
9420 // measuring on line wrapping boundaries.
9421 if (webkit && options.lineWrapping &&
9422 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9423 { display.lineDiv.style.textRendering = "auto"; }
9424 }
9425
9426 // The default configuration options.
9427 CodeMirror$1.defaults = defaults;
9428 // Functions to run when options are changed.
9429 CodeMirror$1.optionHandlers = optionHandlers;
9430
9431 // Attach the necessary event handlers when initializing the editor
9432 function registerEventHandlers(cm) {
9433 var d = cm.display;
9434 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9435 // Older IE's will not fire a second mousedown for a double click
9436 if (ie && ie_version < 11)
9437 { on(d.scroller, "dblclick", operation(cm, function (e) {
9438 if (signalDOMEvent(cm, e)) { return }
9439 var pos = posFromMouse(cm, e);
9440 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9441 e_preventDefault(e);
9442 var word = cm.findWordAt(pos);
9443 extendSelection(cm.doc, word.anchor, word.head);
9444 })); }
9445 else
9446 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9447 // Some browsers fire contextmenu *after* opening the menu, at
9448 // which point we can't mess with it anymore. Context menu is
9449 // handled in onMouseDown for these browsers.
9450 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); }
9451
9452 // Used to suppress mouse event handling when a touch happens
9453 var touchFinished, prevTouch = {end: 0};
9454 function finishTouch() {
9455 if (d.activeTouch) {
9456 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9457 prevTouch = d.activeTouch;
9458 prevTouch.end = +new Date;
9459 }
9460 }
9461 function isMouseLikeTouchEvent(e) {
9462 if (e.touches.length != 1) { return false }
9463 var touch = e.touches[0];
9464 return touch.radiusX <= 1 && touch.radiusY <= 1
9465 }
9466 function farAway(touch, other) {
9467 if (other.left == null) { return true }
9468 var dx = other.left - touch.left, dy = other.top - touch.top;
9469 return dx * dx + dy * dy > 20 * 20
9470 }
9471 on(d.scroller, "touchstart", function (e) {
9472 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9473 d.input.ensurePolled();
9474 clearTimeout(touchFinished);
9475 var now = +new Date;
9476 d.activeTouch = {start: now, moved: false,
9477 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9478 if (e.touches.length == 1) {
9479 d.activeTouch.left = e.touches[0].pageX;
9480 d.activeTouch.top = e.touches[0].pageY;
9481 }
9482 }
9483 });
9484 on(d.scroller, "touchmove", function () {
9485 if (d.activeTouch) { d.activeTouch.moved = true; }
9486 });
9487 on(d.scroller, "touchend", function (e) {
9488 var touch = d.activeTouch;
9489 if (touch && !eventInWidget(d, e) && touch.left != null &&
9490 !touch.moved && new Date - touch.start < 300) {
9491 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9492 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9493 { range = new Range(pos, pos); }
9494 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9495 { range = cm.findWordAt(pos); }
9496 else // Triple tap
9497 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9498 cm.setSelection(range.anchor, range.head);
9499 cm.focus();
9500 e_preventDefault(e);
9501 }
9502 finishTouch();
9503 });
9504 on(d.scroller, "touchcancel", finishTouch);
9505
9506 // Sync scrolling between fake scrollbars and real scrollable
9507 // area, ensure viewport is updated when scrolling.
9508 on(d.scroller, "scroll", function () {
9509 if (d.scroller.clientHeight) {
9510 updateScrollTop(cm, d.scroller.scrollTop);
9511 setScrollLeft(cm, d.scroller.scrollLeft, true);
9512 signal(cm, "scroll", cm);
9513 }
9514 });
9515
9516 // Listen to wheel events in order to try and update the viewport on time.
9517 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9518 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9519
9520 // Prevent wrapper from ever scrolling
9521 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9522
9523 d.dragFunctions = {
9524 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9525 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9526 start: function (e) { return onDragStart(cm, e); },
9527 drop: operation(cm, onDrop),
9528 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9529 };
9530
9531 var inp = d.input.getField();
9532 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9533 on(inp, "keydown", operation(cm, onKeyDown));
9534 on(inp, "keypress", operation(cm, onKeyPress));
9535 on(inp, "focus", function (e) { return onFocus(cm, e); });
9536 on(inp, "blur", function (e) { return onBlur(cm, e); });
9537 }
9538
9539 var initHooks = [];
9540 CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); };
9541
9542 // Indent the given line. The how parameter can be "smart",
9543 // "add"/null, "subtract", or "prev". When aggressive is false
9544 // (typically set to true for forced single-line indents), empty
9545 // lines are not indented, and places where the mode returns Pass
9546 // are left alone.
9547 function indentLine(cm, n, how, aggressive) {
9548 var doc = cm.doc, state;
9549 if (how == null) { how = "add"; }
9550 if (how == "smart") {
9551 // Fall back to "prev" when the mode doesn't have an indentation
9552 // method.
9553 if (!doc.mode.indent) { how = "prev"; }
9554 else { state = getContextBefore(cm, n).state; }
9555 }
9556
9557 var tabSize = cm.options.tabSize;
9558 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9559 if (line.stateAfter) { line.stateAfter = null; }
9560 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9561 if (!aggressive && !/\S/.test(line.text)) {
9562 indentation = 0;
9563 how = "not";
9564 } else if (how == "smart") {
9565 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9566 if (indentation == Pass || indentation > 150) {
9567 if (!aggressive) { return }
9568 how = "prev";
9569 }
9570 }
9571 if (how == "prev") {
9572 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9573 else { indentation = 0; }
9574 } else if (how == "add") {
9575 indentation = curSpace + cm.options.indentUnit;
9576 } else if (how == "subtract") {
9577 indentation = curSpace - cm.options.indentUnit;
9578 } else if (typeof how == "number") {
9579 indentation = curSpace + how;
9580 }
9581 indentation = Math.max(0, indentation);
9582
9583 var indentString = "", pos = 0;
9584 if (cm.options.indentWithTabs)
9585 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9586 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9587
9588 if (indentString != curSpaceString) {
9589 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9590 line.stateAfter = null;
9591 return true
9592 } else {
9593 // Ensure that, if the cursor was in the whitespace at the start
9594 // of the line, it is moved to the end of that space.
9595 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9596 var range = doc.sel.ranges[i$1];
9597 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9598 var pos$1 = Pos(n, curSpaceString.length);
9599 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9600 break
9601 }
9602 }
9603 }
9604 }
9605
9606 // This will be set to a {lineWise: bool, text: [string]} object, so
9607 // that, when pasting, we know what kind of selections the copied
9608 // text was made out of.
9609 var lastCopied = null;
9610
9611 function setLastCopied(newLastCopied) {
9612 lastCopied = newLastCopied;
9613 }
9614
9615 function applyTextInput(cm, inserted, deleted, sel, origin) {
9616 var doc = cm.doc;
9617 cm.display.shift = false;
9618 if (!sel) { sel = doc.sel; }
9619
9620 var paste = cm.state.pasteIncoming || origin == "paste";
9621 var textLines = splitLinesAuto(inserted), multiPaste = null;
9622 // When pasting N lines into N selections, insert one line per selection
9623 if (paste && sel.ranges.length > 1) {
9624 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9625 if (sel.ranges.length % lastCopied.text.length == 0) {
9626 multiPaste = [];
9627 for (var i = 0; i < lastCopied.text.length; i++)
9628 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9629 }
9630 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9631 multiPaste = map(textLines, function (l) { return [l]; });
9632 }
9633 }
9634
9635 var updateInput;
9636 // Normal behavior is to insert the new text into every selection
9637 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9638 var range$$1 = sel.ranges[i$1];
9639 var from = range$$1.from(), to = range$$1.to();
9640 if (range$$1.empty()) {
9641 if (deleted && deleted > 0) // Handle deletion
9642 { from = Pos(from.line, from.ch - deleted); }
9643 else if (cm.state.overwrite && !paste) // Handle overwrite
9644 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9645 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9646 { from = to = Pos(from.line, 0); }
9647 }
9648 updateInput = cm.curOp.updateInput;
9649 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9650 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
9651 makeChange(cm.doc, changeEvent);
9652 signalLater(cm, "inputRead", cm, changeEvent);
9653 }
9654 if (inserted && !paste)
9655 { triggerElectric(cm, inserted); }
9656
9657 ensureCursorVisible(cm);
9658 cm.curOp.updateInput = updateInput;
9659 cm.curOp.typing = true;
9660 cm.state.pasteIncoming = cm.state.cutIncoming = false;
9661 }
9662
9663 function handlePaste(e, cm) {
9664 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9665 if (pasted) {
9666 e.preventDefault();
9667 if (!cm.isReadOnly() && !cm.options.disableInput)
9668 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9669 return true
9670 }
9671 }
9672
9673 function triggerElectric(cm, inserted) {
9674 // When an 'electric' character is inserted, immediately trigger a reindent
9675 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9676 var sel = cm.doc.sel;
9677
9678 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9679 var range$$1 = sel.ranges[i];
9680 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9681 var mode = cm.getModeAt(range$$1.head);
9682 var indented = false;
9683 if (mode.electricChars) {
9684 for (var j = 0; j < mode.electricChars.length; j++)
9685 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9686 indented = indentLine(cm, range$$1.head.line, "smart");
9687 break
9688 } }
9689 } else if (mode.electricInput) {
9690 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9691 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9692 }
9693 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9694 }
9695 }
9696
9697 function copyableRanges(cm) {
9698 var text = [], ranges = [];
9699 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9700 var line = cm.doc.sel.ranges[i].head.line;
9701 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9702 ranges.push(lineRange);
9703 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9704 }
9705 return {text: text, ranges: ranges}
9706 }
9707
9708 function disableBrowserMagic(field, spellcheck) {
9709 field.setAttribute("autocorrect", "off");
9710 field.setAttribute("autocapitalize", "off");
9711 field.setAttribute("spellcheck", !!spellcheck);
9712 }
9713
9714 function hiddenTextarea() {
9715 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9716 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9717 // The textarea is kept positioned near the cursor to prevent the
9718 // fact that it'll be scrolled into view on input from scrolling
9719 // our fake cursor out of view. On webkit, when wrap=off, paste is
9720 // very slow. So make the area wide instead.
9721 if (webkit) { te.style.width = "1000px"; }
9722 else { te.setAttribute("wrap", "off"); }
9723 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9724 if (ios) { te.style.border = "1px solid black"; }
9725 disableBrowserMagic(te);
9726 return div
9727 }
9728
9729 // The publicly visible API. Note that methodOp(f) means
9730 // 'wrap f in an operation, performed on its `this` parameter'.
9731
9732 // This is not the complete set of editor methods. Most of the
9733 // methods defined on the Doc type are also injected into
9734 // CodeMirror.prototype, for backwards compatibility and
9735 // convenience.
9736
9737 var addEditorMethods = function(CodeMirror) {
9738 var optionHandlers = CodeMirror.optionHandlers;
9739
9740 var helpers = CodeMirror.helpers = {};
9741
9742 CodeMirror.prototype = {
9743 constructor: CodeMirror,
9744 focus: function(){window.focus(); this.display.input.focus();},
9745
9746 setOption: function(option, value) {
9747 var options = this.options, old = options[option];
9748 if (options[option] == value && option != "mode") { return }
9749 options[option] = value;
9750 if (optionHandlers.hasOwnProperty(option))
9751 { operation(this, optionHandlers[option])(this, value, old); }
9752 signal(this, "optionChange", this, option);
9753 },
9754
9755 getOption: function(option) {return this.options[option]},
9756 getDoc: function() {return this.doc},
9757
9758 addKeyMap: function(map$$1, bottom) {
9759 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9760 },
9761 removeKeyMap: function(map$$1) {
9762 var maps = this.state.keyMaps;
9763 for (var i = 0; i < maps.length; ++i)
9764 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9765 maps.splice(i, 1);
9766 return true
9767 } }
9768 },
9769
9770 addOverlay: methodOp(function(spec, options) {
9771 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9772 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9773 insertSorted(this.state.overlays,
9774 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9775 priority: (options && options.priority) || 0},
9776 function (overlay) { return overlay.priority; });
9777 this.state.modeGen++;
9778 regChange(this);
9779 }),
9780 removeOverlay: methodOp(function(spec) {
9781 var this$1 = this;
9782
9783 var overlays = this.state.overlays;
9784 for (var i = 0; i < overlays.length; ++i) {
9785 var cur = overlays[i].modeSpec;
9786 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9787 overlays.splice(i, 1);
9788 this$1.state.modeGen++;
9789 regChange(this$1);
9790 return
9791 }
9792 }
9793 }),
9794
9795 indentLine: methodOp(function(n, dir, aggressive) {
9796 if (typeof dir != "string" && typeof dir != "number") {
9797 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9798 else { dir = dir ? "add" : "subtract"; }
9799 }
9800 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9801 }),
9802 indentSelection: methodOp(function(how) {
9803 var this$1 = this;
9804
9805 var ranges = this.doc.sel.ranges, end = -1;
9806 for (var i = 0; i < ranges.length; i++) {
9807 var range$$1 = ranges[i];
9808 if (!range$$1.empty()) {
9809 var from = range$$1.from(), to = range$$1.to();
9810 var start = Math.max(end, from.line);
9811 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9812 for (var j = start; j < end; ++j)
9813 { indentLine(this$1, j, how); }
9814 var newRanges = this$1.doc.sel.ranges;
9815 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9816 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9817 } else if (range$$1.head.line > end) {
9818 indentLine(this$1, range$$1.head.line, how, true);
9819 end = range$$1.head.line;
9820 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9821 }
9822 }
9823 }),
9824
9825 // Fetch the parser token for a given character. Useful for hacks
9826 // that want to inspect the mode state (say, for completion).
9827 getTokenAt: function(pos, precise) {
9828 return takeToken(this, pos, precise)
9829 },
9830
9831 getLineTokens: function(line, precise) {
9832 return takeToken(this, Pos(line), precise, true)
9833 },
9834
9835 getTokenTypeAt: function(pos) {
9836 pos = clipPos(this.doc, pos);
9837 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9838 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9839 var type;
9840 if (ch == 0) { type = styles[2]; }
9841 else { for (;;) {
9842 var mid = (before + after) >> 1;
9843 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9844 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9845 else { type = styles[mid * 2 + 2]; break }
9846 } }
9847 var cut = type ? type.indexOf("overlay ") : -1;
9848 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9849 },
9850
9851 getModeAt: function(pos) {
9852 var mode = this.doc.mode;
9853 if (!mode.innerMode) { return mode }
9854 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9855 },
9856
9857 getHelper: function(pos, type) {
9858 return this.getHelpers(pos, type)[0]
9859 },
9860
9861 getHelpers: function(pos, type) {
9862 var this$1 = this;
9863
9864 var found = [];
9865 if (!helpers.hasOwnProperty(type)) { return found }
9866 var help = helpers[type], mode = this.getModeAt(pos);
9867 if (typeof mode[type] == "string") {
9868 if (help[mode[type]]) { found.push(help[mode[type]]); }
9869 } else if (mode[type]) {
9870 for (var i = 0; i < mode[type].length; i++) {
9871 var val = help[mode[type][i]];
9872 if (val) { found.push(val); }
9873 }
9874 } else if (mode.helperType && help[mode.helperType]) {
9875 found.push(help[mode.helperType]);
9876 } else if (help[mode.name]) {
9877 found.push(help[mode.name]);
9878 }
9879 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9880 var cur = help._global[i$1];
9881 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9882 { found.push(cur.val); }
9883 }
9884 return found
9885 },
9886
9887 getStateAfter: function(line, precise) {
9888 var doc = this.doc;
9889 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9890 return getContextBefore(this, line + 1, precise).state
9891 },
9892
9893 cursorCoords: function(start, mode) {
9894 var pos, range$$1 = this.doc.sel.primary();
9895 if (start == null) { pos = range$$1.head; }
9896 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9897 else { pos = start ? range$$1.from() : range$$1.to(); }
9898 return cursorCoords(this, pos, mode || "page")
9899 },
9900
9901 charCoords: function(pos, mode) {
9902 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9903 },
9904
9905 coordsChar: function(coords, mode) {
9906 coords = fromCoordSystem(this, coords, mode || "page");
9907 return coordsChar(this, coords.left, coords.top)
9908 },
9909
9910 lineAtHeight: function(height, mode) {
9911 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9912 return lineAtHeight(this.doc, height + this.display.viewOffset)
9913 },
9914 heightAtLine: function(line, mode, includeWidgets) {
9915 var end = false, lineObj;
9916 if (typeof line == "number") {
9917 var last = this.doc.first + this.doc.size - 1;
9918 if (line < this.doc.first) { line = this.doc.first; }
9919 else if (line > last) { line = last; end = true; }
9920 lineObj = getLine(this.doc, line);
9921 } else {
9922 lineObj = line;
9923 }
9924 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9925 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9926 },
9927
9928 defaultTextHeight: function() { return textHeight(this.display) },
9929 defaultCharWidth: function() { return charWidth(this.display) },
9930
9931 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9932
9933 addWidget: function(pos, node, scroll, vert, horiz) {
9934 var display = this.display;
9935 pos = cursorCoords(this, clipPos(this.doc, pos));
9936 var top = pos.bottom, left = pos.left;
9937 node.style.position = "absolute";
9938 node.setAttribute("cm-ignore-events", "true");
9939 this.display.input.setUneditable(node);
9940 display.sizer.appendChild(node);
9941 if (vert == "over") {
9942 top = pos.top;
9943 } else if (vert == "above" || vert == "near") {
9944 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9945 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
9946 // Default to positioning above (if specified and possible); otherwise default to positioning below
9947 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
9948 { top = pos.top - node.offsetHeight; }
9949 else if (pos.bottom + node.offsetHeight <= vspace)
9950 { top = pos.bottom; }
9951 if (left + node.offsetWidth > hspace)
9952 { left = hspace - node.offsetWidth; }
9953 }
9954 node.style.top = top + "px";
9955 node.style.left = node.style.right = "";
9956 if (horiz == "right") {
9957 left = display.sizer.clientWidth - node.offsetWidth;
9958 node.style.right = "0px";
9959 } else {
9960 if (horiz == "left") { left = 0; }
9961 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
9962 node.style.left = left + "px";
9963 }
9964 if (scroll)
9965 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
9966 },
9967
9968 triggerOnKeyDown: methodOp(onKeyDown),
9969 triggerOnKeyPress: methodOp(onKeyPress),
9970 triggerOnKeyUp: onKeyUp,
9971 triggerOnMouseDown: methodOp(onMouseDown),
9972
9973 execCommand: function(cmd) {
9974 if (commands.hasOwnProperty(cmd))
9975 { return commands[cmd].call(null, this) }
9976 },
9977
9978 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
9979
9980 findPosH: function(from, amount, unit, visually) {
9981 var this$1 = this;
9982
9983 var dir = 1;
9984 if (amount < 0) { dir = -1; amount = -amount; }
9985 var cur = clipPos(this.doc, from);
9986 for (var i = 0; i < amount; ++i) {
9987 cur = findPosH(this$1.doc, cur, dir, unit, visually);
9988 if (cur.hitSide) { break }
9989 }
9990 return cur
9991 },
9992
9993 moveH: methodOp(function(dir, unit) {
9994 var this$1 = this;
9995
9996 this.extendSelectionsBy(function (range$$1) {
9997 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
9998 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
9999 else
10000 { return dir < 0 ? range$$1.from() : range$$1.to() }
10001 }, sel_move);
10002 }),
10003
10004 deleteH: methodOp(function(dir, unit) {
10005 var sel = this.doc.sel, doc = this.doc;
10006 if (sel.somethingSelected())
10007 { doc.replaceSelection("", null, "+delete"); }
10008 else
10009 { deleteNearSelection(this, function (range$$1) {
10010 var other = findPosH(doc, range$$1.head, dir, unit, false);
10011 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
10012 }); }
10013 }),
10014
10015 findPosV: function(from, amount, unit, goalColumn) {
10016 var this$1 = this;
10017
10018 var dir = 1, x = goalColumn;
10019 if (amount < 0) { dir = -1; amount = -amount; }
10020 var cur = clipPos(this.doc, from);
10021 for (var i = 0; i < amount; ++i) {
10022 var coords = cursorCoords(this$1, cur, "div");
10023 if (x == null) { x = coords.left; }
10024 else { coords.left = x; }
10025 cur = findPosV(this$1, coords, dir, unit);
10026 if (cur.hitSide) { break }
10027 }
10028 return cur
10029 },
10030
10031 moveV: methodOp(function(dir, unit) {
10032 var this$1 = this;
10033
10034 var doc = this.doc, goals = [];
10035 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
10036 doc.extendSelectionsBy(function (range$$1) {
10037 if (collapse)
10038 { return dir < 0 ? range$$1.from() : range$$1.to() }
10039 var headPos = cursorCoords(this$1, range$$1.head, "div");
10040 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
10041 goals.push(headPos.left);
10042 var pos = findPosV(this$1, headPos, dir, unit);
10043 if (unit == "page" && range$$1 == doc.sel.primary())
10044 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
10045 return pos
10046 }, sel_move);
10047 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
10048 { doc.sel.ranges[i].goalColumn = goals[i]; } }
10049 }),
10050
10051 // Find the word at the given position (as returned by coordsChar).
10052 findWordAt: function(pos) {
10053 var doc = this.doc, line = getLine(doc, pos.line).text;
10054 var start = pos.ch, end = pos.ch;
10055 if (line) {
10056 var helper = this.getHelper(pos, "wordChars");
10057 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
10058 var startChar = line.charAt(start);
10059 var check = isWordChar(startChar, helper)
10060 ? function (ch) { return isWordChar(ch, helper); }
10061 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
10062 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
10063 while (start > 0 && check(line.charAt(start - 1))) { --start; }
10064 while (end < line.length && check(line.charAt(end))) { ++end; }
10065 }
10066 return new Range(Pos(pos.line, start), Pos(pos.line, end))
10067 },
10068
10069 toggleOverwrite: function(value) {
10070 if (value != null && value == this.state.overwrite) { return }
10071 if (this.state.overwrite = !this.state.overwrite)
10072 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10073 else
10074 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10075
10076 signal(this, "overwriteToggle", this, this.state.overwrite);
10077 },
10078 hasFocus: function() { return this.display.input.getField() == activeElt() },
10079 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
10080
10081 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
10082 getScrollInfo: function() {
10083 var scroller = this.display.scroller;
10084 return {left: scroller.scrollLeft, top: scroller.scrollTop,
10085 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
10086 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
10087 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
10088 },
10089
10090 scrollIntoView: methodOp(function(range$$1, margin) {
10091 if (range$$1 == null) {
10092 range$$1 = {from: this.doc.sel.primary().head, to: null};
10093 if (margin == null) { margin = this.options.cursorScrollMargin; }
10094 } else if (typeof range$$1 == "number") {
10095 range$$1 = {from: Pos(range$$1, 0), to: null};
10096 } else if (range$$1.from == null) {
10097 range$$1 = {from: range$$1, to: null};
10098 }
10099 if (!range$$1.to) { range$$1.to = range$$1.from; }
10100 range$$1.margin = margin || 0;
10101
10102 if (range$$1.from.line != null) {
10103 scrollToRange(this, range$$1);
10104 } else {
10105 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
10106 }
10107 }),
10108
10109 setSize: methodOp(function(width, height) {
10110 var this$1 = this;
10111
10112 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
10113 if (width != null) { this.display.wrapper.style.width = interpret(width); }
10114 if (height != null) { this.display.wrapper.style.height = interpret(height); }
10115 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
10116 var lineNo$$1 = this.display.viewFrom;
10117 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
10118 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
10119 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
10120 ++lineNo$$1;
10121 });
10122 this.curOp.forceUpdate = true;
10123 signal(this, "refresh", this);
10124 }),
10125
10126 operation: function(f){return runInOp(this, f)},
10127 startOperation: function(){return startOperation(this)},
10128 endOperation: function(){return endOperation(this)},
10129
10130 refresh: methodOp(function() {
10131 var oldHeight = this.display.cachedTextHeight;
10132 regChange(this);
10133 this.curOp.forceUpdate = true;
10134 clearCaches(this);
10135 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10136 updateGutterSpace(this);
10137 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
10138 { estimateLineHeights(this); }
10139 signal(this, "refresh", this);
10140 }),
10141
10142 swapDoc: methodOp(function(doc) {
10143 var old = this.doc;
10144 old.cm = null;
10145 attachDoc(this, doc);
10146 clearCaches(this);
10147 this.display.input.reset();
10148 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10149 this.curOp.forceScroll = true;
10150 signalLater(this, "swapDoc", this, old);
10151 return old
10152 }),
10153
10154 getInputField: function(){return this.display.input.getField()},
10155 getWrapperElement: function(){return this.display.wrapper},
10156 getScrollerElement: function(){return this.display.scroller},
10157 getGutterElement: function(){return this.display.gutters}
10158 };
10159 eventMixin(CodeMirror);
10160
10161 CodeMirror.registerHelper = function(type, name, value) {
10162 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10163 helpers[type][name] = value;
10164 };
10165 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10166 CodeMirror.registerHelper(type, name, value);
10167 helpers[type]._global.push({pred: predicate, val: value});
10168 };
10169 };
10170
10171 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10172 // right), unit can be "char", "column" (like char, but doesn't
10173 // cross line boundaries), "word" (across next word), or "group" (to
10174 // the start of next group of word or non-word-non-whitespace
10175 // chars). The visually param controls whether, in right-to-left
10176 // text, direction 1 means to move towards the next index in the
10177 // string, or towards the character to the right of the current
10178 // position. The resulting position will have a hitSide=true
10179 // property if it reached the end of the document.
10180 function findPosH(doc, pos, dir, unit, visually) {
10181 var oldPos = pos;
10182 var origDir = dir;
10183 var lineObj = getLine(doc, pos.line);
10184 function findNextLine() {
10185 var l = pos.line + dir;
10186 if (l < doc.first || l >= doc.first + doc.size) { return false }
10187 pos = new Pos(l, pos.ch, pos.sticky);
10188 return lineObj = getLine(doc, l)
10189 }
10190 function moveOnce(boundToLine) {
10191 var next;
10192 if (visually) {
10193 next = moveVisually(doc.cm, lineObj, pos, dir);
10194 } else {
10195 next = moveLogically(lineObj, pos, dir);
10196 }
10197 if (next == null) {
10198 if (!boundToLine && findNextLine())
10199 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
10200 else
10201 { return false }
10202 } else {
10203 pos = next;
10204 }
10205 return true
10206 }
10207
10208 if (unit == "char") {
10209 moveOnce();
10210 } else if (unit == "column") {
10211 moveOnce(true);
10212 } else if (unit == "word" || unit == "group") {
10213 var sawType = null, group = unit == "group";
10214 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10215 for (var first = true;; first = false) {
10216 if (dir < 0 && !moveOnce(!first)) { break }
10217 var cur = lineObj.text.charAt(pos.ch) || "\n";
10218 var type = isWordChar(cur, helper) ? "w"
10219 : group && cur == "\n" ? "n"
10220 : !group || /\s/.test(cur) ? null
10221 : "p";
10222 if (group && !first && !type) { type = "s"; }
10223 if (sawType && sawType != type) {
10224 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10225 break
10226 }
10227
10228 if (type) { sawType = type; }
10229 if (dir > 0 && !moveOnce(!first)) { break }
10230 }
10231 }
10232 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10233 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10234 return result
10235 }
10236
10237 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10238 // "page" or "line". The resulting position will have a hitSide=true
10239 // property if it reached the end of the document.
10240 function findPosV(cm, pos, dir, unit) {
10241 var doc = cm.doc, x = pos.left, y;
10242 if (unit == "page") {
10243 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10244 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10245 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10246
10247 } else if (unit == "line") {
10248 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10249 }
10250 var target;
10251 for (;;) {
10252 target = coordsChar(cm, x, y);
10253 if (!target.outside) { break }
10254 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10255 y += dir * 5;
10256 }
10257 return target
10258 }
10259
10260 // CONTENTEDITABLE INPUT STYLE
10261
10262 var ContentEditableInput = function(cm) {
10263 this.cm = cm;
10264 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10265 this.polling = new Delayed();
10266 this.composing = null;
10267 this.gracePeriod = false;
10268 this.readDOMTimeout = null;
10269 };
10270
10271 ContentEditableInput.prototype.init = function (display) {
10272 var this$1 = this;
10273
10274 var input = this, cm = input.cm;
10275 var div = input.div = display.lineDiv;
10276 disableBrowserMagic(div, cm.options.spellcheck);
10277
10278 on(div, "paste", function (e) {
10279 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10280 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10281 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10282 });
10283
10284 on(div, "compositionstart", function (e) {
10285 this$1.composing = {data: e.data, done: false};
10286 });
10287 on(div, "compositionupdate", function (e) {
10288 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10289 });
10290 on(div, "compositionend", function (e) {
10291 if (this$1.composing) {
10292 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10293 this$1.composing.done = true;
10294 }
10295 });
10296
10297 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10298
10299 on(div, "input", function () {
10300 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10301 });
10302
10303 function onCopyCut(e) {
10304 if (signalDOMEvent(cm, e)) { return }
10305 if (cm.somethingSelected()) {
10306 setLastCopied({lineWise: false, text: cm.getSelections()});
10307 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10308 } else if (!cm.options.lineWiseCopyCut) {
10309 return
10310 } else {
10311 var ranges = copyableRanges(cm);
10312 setLastCopied({lineWise: true, text: ranges.text});
10313 if (e.type == "cut") {
10314 cm.operation(function () {
10315 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10316 cm.replaceSelection("", null, "cut");
10317 });
10318 }
10319 }
10320 if (e.clipboardData) {
10321 e.clipboardData.clearData();
10322 var content = lastCopied.text.join("\n");
10323 // iOS exposes the clipboard API, but seems to discard content inserted into it
10324 e.clipboardData.setData("Text", content);
10325 if (e.clipboardData.getData("Text") == content) {
10326 e.preventDefault();
10327 return
10328 }
10329 }
10330 // Old-fashioned briefly-focus-a-textarea hack
10331 var kludge = hiddenTextarea(), te = kludge.firstChild;
10332 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10333 te.value = lastCopied.text.join("\n");
10334 var hadFocus = document.activeElement;
10335 selectInput(te);
10336 setTimeout(function () {
10337 cm.display.lineSpace.removeChild(kludge);
10338 hadFocus.focus();
10339 if (hadFocus == div) { input.showPrimarySelection(); }
10340 }, 50);
10341 }
10342 on(div, "copy", onCopyCut);
10343 on(div, "cut", onCopyCut);
10344 };
10345
10346 ContentEditableInput.prototype.prepareSelection = function () {
10347 var result = prepareSelection(this.cm, false);
10348 result.focus = this.cm.state.focused;
10349 return result
10350 };
10351
10352 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10353 if (!info || !this.cm.display.view.length) { return }
10354 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10355 this.showMultipleSelections(info);
10356 };
10357
10358 ContentEditableInput.prototype.showPrimarySelection = function () {
10359 var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10360 var from = prim.from(), to = prim.to();
10361
10362 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10363 sel.removeAllRanges();
10364 return
10365 }
10366
10367 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10368 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10369 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10370 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10371 cmp(maxPos(curAnchor, curFocus), to) == 0)
10372 { return }
10373
10374 var view = cm.display.view;
10375 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10376 {node: view[0].measure.map[2], offset: 0};
10377 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10378 if (!end) {
10379 var measure = view[view.length - 1].measure;
10380 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10381 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10382 }
10383
10384 if (!start || !end) {
10385 sel.removeAllRanges();
10386 return
10387 }
10388
10389 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10390 try { rng = range(start.node, start.offset, end.offset, end.node); }
10391 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10392 if (rng) {
10393 if (!gecko && cm.state.focused) {
10394 sel.collapse(start.node, start.offset);
10395 if (!rng.collapsed) {
10396 sel.removeAllRanges();
10397 sel.addRange(rng);
10398 }
10399 } else {
10400 sel.removeAllRanges();
10401 sel.addRange(rng);
10402 }
10403 if (old && sel.anchorNode == null) { sel.addRange(old); }
10404 else if (gecko) { this.startGracePeriod(); }
10405 }
10406 this.rememberSelection();
10407 };
10408
10409 ContentEditableInput.prototype.startGracePeriod = function () {
10410 var this$1 = this;
10411
10412 clearTimeout(this.gracePeriod);
10413 this.gracePeriod = setTimeout(function () {
10414 this$1.gracePeriod = false;
10415 if (this$1.selectionChanged())
10416 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10417 }, 20);
10418 };
10419
10420 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10421 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10422 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10423 };
10424
10425 ContentEditableInput.prototype.rememberSelection = function () {
10426 var sel = window.getSelection();
10427 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10428 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10429 };
10430
10431 ContentEditableInput.prototype.selectionInEditor = function () {
10432 var sel = window.getSelection();
10433 if (!sel.rangeCount) { return false }
10434 var node = sel.getRangeAt(0).commonAncestorContainer;
10435 return contains(this.div, node)
10436 };
10437
10438 ContentEditableInput.prototype.focus = function () {
10439 if (this.cm.options.readOnly != "nocursor") {
10440 if (!this.selectionInEditor())
10441 { this.showSelection(this.prepareSelection(), true); }
10442 this.div.focus();
10443 }
10444 };
10445 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10446 ContentEditableInput.prototype.getField = function () { return this.div };
10447
10448 ContentEditableInput.prototype.supportsTouch = function () { return true };
10449
10450 ContentEditableInput.prototype.receivedFocus = function () {
10451 var input = this;
10452 if (this.selectionInEditor())
10453 { this.pollSelection(); }
10454 else
10455 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10456
10457 function poll() {
10458 if (input.cm.state.focused) {
10459 input.pollSelection();
10460 input.polling.set(input.cm.options.pollInterval, poll);
10461 }
10462 }
10463 this.polling.set(this.cm.options.pollInterval, poll);
10464 };
10465
10466 ContentEditableInput.prototype.selectionChanged = function () {
10467 var sel = window.getSelection();
10468 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10469 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10470 };
10471
10472 ContentEditableInput.prototype.pollSelection = function () {
10473 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10474 var sel = window.getSelection(), cm = this.cm;
10475 // On Android Chrome (version 56, at least), backspacing into an
10476 // uneditable block element will put the cursor in that element,
10477 // and then, because it's not editable, hide the virtual keyboard.
10478 // Because Android doesn't allow us to actually detect backspace
10479 // presses in a sane way, this code checks for when that happens
10480 // and simulates a backspace press in this case.
10481 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
10482 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10483 this.blur();
10484 this.focus();
10485 return
10486 }
10487 if (this.composing) { return }
10488 this.rememberSelection();
10489 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10490 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10491 if (anchor && head) { runInOp(cm, function () {
10492 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10493 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10494 }); }
10495 };
10496
10497 ContentEditableInput.prototype.pollContent = function () {
10498 if (this.readDOMTimeout != null) {
10499 clearTimeout(this.readDOMTimeout);
10500 this.readDOMTimeout = null;
10501 }
10502
10503 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10504 var from = sel.from(), to = sel.to();
10505 if (from.ch == 0 && from.line > cm.firstLine())
10506 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10507 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10508 { to = Pos(to.line + 1, 0); }
10509 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10510
10511 var fromIndex, fromLine, fromNode;
10512 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10513 fromLine = lineNo(display.view[0].line);
10514 fromNode = display.view[0].node;
10515 } else {
10516 fromLine = lineNo(display.view[fromIndex].line);
10517 fromNode = display.view[fromIndex - 1].node.nextSibling;
10518 }
10519 var toIndex = findViewIndex(cm, to.line);
10520 var toLine, toNode;
10521 if (toIndex == display.view.length - 1) {
10522 toLine = display.viewTo - 1;
10523 toNode = display.lineDiv.lastChild;
10524 } else {
10525 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10526 toNode = display.view[toIndex + 1].node.previousSibling;
10527 }
10528
10529 if (!fromNode) { return false }
10530 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10531 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10532 while (newText.length > 1 && oldText.length > 1) {
10533 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10534 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10535 else { break }
10536 }
10537
10538 var cutFront = 0, cutEnd = 0;
10539 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10540 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10541 { ++cutFront; }
10542 var newBot = lst(newText), oldBot = lst(oldText);
10543 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10544 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10545 while (cutEnd < maxCutEnd &&
10546 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10547 { ++cutEnd; }
10548 // Try to move start of change to start of selection if ambiguous
10549 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10550 while (cutFront && cutFront > from.ch &&
10551 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10552 cutFront--;
10553 cutEnd++;
10554 }
10555 }
10556
10557 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10558 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10559
10560 var chFrom = Pos(fromLine, cutFront);
10561 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10562 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10563 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10564 return true
10565 }
10566 };
10567
10568 ContentEditableInput.prototype.ensurePolled = function () {
10569 this.forceCompositionEnd();
10570 };
10571 ContentEditableInput.prototype.reset = function () {
10572 this.forceCompositionEnd();
10573 };
10574 ContentEditableInput.prototype.forceCompositionEnd = function () {
10575 if (!this.composing) { return }
10576 clearTimeout(this.readDOMTimeout);
10577 this.composing = null;
10578 this.updateFromDOM();
10579 this.div.blur();
10580 this.div.focus();
10581 };
10582 ContentEditableInput.prototype.readFromDOMSoon = function () {
10583 var this$1 = this;
10584
10585 if (this.readDOMTimeout != null) { return }
10586 this.readDOMTimeout = setTimeout(function () {
10587 this$1.readDOMTimeout = null;
10588 if (this$1.composing) {
10589 if (this$1.composing.done) { this$1.composing = null; }
10590 else { return }
10591 }
10592 this$1.updateFromDOM();
10593 }, 80);
10594 };
10595
10596 ContentEditableInput.prototype.updateFromDOM = function () {
10597 var this$1 = this;
10598
10599 if (this.cm.isReadOnly() || !this.pollContent())
10600 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10601 };
10602
10603 ContentEditableInput.prototype.setUneditable = function (node) {
10604 node.contentEditable = "false";
10605 };
10606
10607 ContentEditableInput.prototype.onKeyPress = function (e) {
10608 if (e.charCode == 0) { return }
10609 e.preventDefault();
10610 if (!this.cm.isReadOnly())
10611 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10612 };
10613
10614 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10615 this.div.contentEditable = String(val != "nocursor");
10616 };
10617
10618 ContentEditableInput.prototype.onContextMenu = function () {};
10619 ContentEditableInput.prototype.resetPosition = function () {};
10620
10621 ContentEditableInput.prototype.needsContentAttribute = true;
10622
10623 function posToDOM(cm, pos) {
10624 var view = findViewForLine(cm, pos.line);
10625 if (!view || view.hidden) { return null }
10626 var line = getLine(cm.doc, pos.line);
10627 var info = mapFromLineView(view, line, pos.line);
10628
10629 var order = getOrder(line, cm.doc.direction), side = "left";
10630 if (order) {
10631 var partPos = getBidiPartAt(order, pos.ch);
10632 side = partPos % 2 ? "right" : "left";
10633 }
10634 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10635 result.offset = result.collapse == "right" ? result.end : result.start;
10636 return result
10637 }
10638
10639 function isInGutter(node) {
10640 for (var scan = node; scan; scan = scan.parentNode)
10641 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10642 return false
10643 }
10644
10645 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10646
10647 function domTextBetween(cm, from, to, fromLine, toLine) {
10648 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
10649 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10650 function close() {
10651 if (closing) {
10652 text += lineSep;
10653 closing = false;
10654 }
10655 }
10656 function addText(str) {
10657 if (str) {
10658 close();
10659 text += str;
10660 }
10661 }
10662 function walk(node) {
10663 if (node.nodeType == 1) {
10664 var cmText = node.getAttribute("cm-text");
10665 if (cmText != null) {
10666 addText(cmText || node.textContent.replace(/\u200b/g, ""));
10667 return
10668 }
10669 var markerID = node.getAttribute("cm-marker"), range$$1;
10670 if (markerID) {
10671 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10672 if (found.length && (range$$1 = found[0].find(0)))
10673 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10674 return
10675 }
10676 if (node.getAttribute("contenteditable") == "false") { return }
10677 var isBlock = /^(pre|div|p)$/i.test(node.nodeName);
10678 if (isBlock) { close(); }
10679 for (var i = 0; i < node.childNodes.length; i++)
10680 { walk(node.childNodes[i]); }
10681 if (isBlock) { closing = true; }
10682 } else if (node.nodeType == 3) {
10683 addText(node.nodeValue);
10684 }
10685 }
10686 for (;;) {
10687 walk(from);
10688 if (from == to) { break }
10689 from = from.nextSibling;
10690 }
10691 return text
10692 }
10693
10694 function domToPos(cm, node, offset) {
10695 var lineNode;
10696 if (node == cm.display.lineDiv) {
10697 lineNode = cm.display.lineDiv.childNodes[offset];
10698 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10699 node = null; offset = 0;
10700 } else {
10701 for (lineNode = node;; lineNode = lineNode.parentNode) {
10702 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10703 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10704 }
10705 }
10706 for (var i = 0; i < cm.display.view.length; i++) {
10707 var lineView = cm.display.view[i];
10708 if (lineView.node == lineNode)
10709 { return locateNodeInLineView(lineView, node, offset) }
10710 }
10711 }
10712
10713 function locateNodeInLineView(lineView, node, offset) {
10714 var wrapper = lineView.text.firstChild, bad = false;
10715 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10716 if (node == wrapper) {
10717 bad = true;
10718 node = wrapper.childNodes[offset];
10719 offset = 0;
10720 if (!node) {
10721 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10722 return badPos(Pos(lineNo(line), line.text.length), bad)
10723 }
10724 }
10725
10726 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10727 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10728 textNode = node.firstChild;
10729 if (offset) { offset = textNode.nodeValue.length; }
10730 }
10731 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10732 var measure = lineView.measure, maps = measure.maps;
10733
10734 function find(textNode, topNode, offset) {
10735 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10736 var map$$1 = i < 0 ? measure.map : maps[i];
10737 for (var j = 0; j < map$$1.length; j += 3) {
10738 var curNode = map$$1[j + 2];
10739 if (curNode == textNode || curNode == topNode) {
10740 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10741 var ch = map$$1[j] + offset;
10742 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10743 return Pos(line, ch)
10744 }
10745 }
10746 }
10747 }
10748 var found = find(textNode, topNode, offset);
10749 if (found) { return badPos(found, bad) }
10750
10751 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10752 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10753 found = find(after, after.firstChild, 0);
10754 if (found)
10755 { return badPos(Pos(found.line, found.ch - dist), bad) }
10756 else
10757 { dist += after.textContent.length; }
10758 }
10759 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10760 found = find(before, before.firstChild, -1);
10761 if (found)
10762 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10763 else
10764 { dist$1 += before.textContent.length; }
10765 }
10766 }
10767
10768 // TEXTAREA INPUT STYLE
10769
10770 var TextareaInput = function(cm) {
10771 this.cm = cm;
10772 // See input.poll and input.reset
10773 this.prevInput = "";
10774
10775 // Flag that indicates whether we expect input to appear real soon
10776 // now (after some event like 'keypress' or 'input') and are
10777 // polling intensively.
10778 this.pollingFast = false;
10779 // Self-resetting timeout for the poller
10780 this.polling = new Delayed();
10781 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10782 this.hasSelection = false;
10783 this.composing = null;
10784 };
10785
10786 TextareaInput.prototype.init = function (display) {
10787 var this$1 = this;
10788
10789 var input = this, cm = this.cm;
10790
10791 // Wraps and hides input textarea
10792 var div = this.wrapper = hiddenTextarea();
10793 // The semihidden textarea that is focused when the editor is
10794 // focused, and receives input.
10795 var te = this.textarea = div.firstChild;
10796 display.wrapper.insertBefore(div, display.wrapper.firstChild);
10797
10798 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10799 if (ios) { te.style.width = "0px"; }
10800
10801 on(te, "input", function () {
10802 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10803 input.poll();
10804 });
10805
10806 on(te, "paste", function (e) {
10807 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10808
10809 cm.state.pasteIncoming = true;
10810 input.fastPoll();
10811 });
10812
10813 function prepareCopyCut(e) {
10814 if (signalDOMEvent(cm, e)) { return }
10815 if (cm.somethingSelected()) {
10816 setLastCopied({lineWise: false, text: cm.getSelections()});
10817 } else if (!cm.options.lineWiseCopyCut) {
10818 return
10819 } else {
10820 var ranges = copyableRanges(cm);
10821 setLastCopied({lineWise: true, text: ranges.text});
10822 if (e.type == "cut") {
10823 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10824 } else {
10825 input.prevInput = "";
10826 te.value = ranges.text.join("\n");
10827 selectInput(te);
10828 }
10829 }
10830 if (e.type == "cut") { cm.state.cutIncoming = true; }
10831 }
10832 on(te, "cut", prepareCopyCut);
10833 on(te, "copy", prepareCopyCut);
10834
10835 on(display.scroller, "paste", function (e) {
10836 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10837 cm.state.pasteIncoming = true;
10838 input.focus();
10839 });
10840
10841 // Prevent normal selection in the editor (we handle our own)
10842 on(display.lineSpace, "selectstart", function (e) {
10843 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10844 });
10845
10846 on(te, "compositionstart", function () {
10847 var start = cm.getCursor("from");
10848 if (input.composing) { input.composing.range.clear(); }
10849 input.composing = {
10850 start: start,
10851 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10852 };
10853 });
10854 on(te, "compositionend", function () {
10855 if (input.composing) {
10856 input.poll();
10857 input.composing.range.clear();
10858 input.composing = null;
10859 }
10860 });
10861 };
10862
10863 TextareaInput.prototype.prepareSelection = function () {
10864 // Redraw the selection and/or cursor
10865 var cm = this.cm, display = cm.display, doc = cm.doc;
10866 var result = prepareSelection(cm);
10867
10868 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10869 if (cm.options.moveInputWithCursor) {
10870 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10871 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10872 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10873 headPos.top + lineOff.top - wrapOff.top));
10874 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10875 headPos.left + lineOff.left - wrapOff.left));
10876 }
10877
10878 return result
10879 };
10880
10881 TextareaInput.prototype.showSelection = function (drawn) {
10882 var cm = this.cm, display = cm.display;
10883 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10884 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10885 if (drawn.teTop != null) {
10886 this.wrapper.style.top = drawn.teTop + "px";
10887 this.wrapper.style.left = drawn.teLeft + "px";
10888 }
10889 };
10890
10891 // Reset the input to correspond to the selection (or to be empty,
10892 // when not typing and nothing is selected)
10893 TextareaInput.prototype.reset = function (typing) {
10894 if (this.contextMenuPending || this.composing) { return }
10895 var cm = this.cm;
10896 if (cm.somethingSelected()) {
10897 this.prevInput = "";
10898 var content = cm.getSelection();
10899 this.textarea.value = content;
10900 if (cm.state.focused) { selectInput(this.textarea); }
10901 if (ie && ie_version >= 9) { this.hasSelection = content; }
10902 } else if (!typing) {
10903 this.prevInput = this.textarea.value = "";
10904 if (ie && ie_version >= 9) { this.hasSelection = null; }
10905 }
10906 };
10907
10908 TextareaInput.prototype.getField = function () { return this.textarea };
10909
10910 TextareaInput.prototype.supportsTouch = function () { return false };
10911
10912 TextareaInput.prototype.focus = function () {
10913 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10914 try { this.textarea.focus(); }
10915 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
10916 }
10917 };
10918
10919 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
10920
10921 TextareaInput.prototype.resetPosition = function () {
10922 this.wrapper.style.top = this.wrapper.style.left = 0;
10923 };
10924
10925 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
10926
10927 // Poll for input changes, using the normal rate of polling. This
10928 // runs as long as the editor is focused.
10929 TextareaInput.prototype.slowPoll = function () {
10930 var this$1 = this;
10931
10932 if (this.pollingFast) { return }
10933 this.polling.set(this.cm.options.pollInterval, function () {
10934 this$1.poll();
10935 if (this$1.cm.state.focused) { this$1.slowPoll(); }
10936 });
10937 };
10938
10939 // When an event has just come in that is likely to add or change
10940 // something in the input textarea, we poll faster, to ensure that
10941 // the change appears on the screen quickly.
10942 TextareaInput.prototype.fastPoll = function () {
10943 var missed = false, input = this;
10944 input.pollingFast = true;
10945 function p() {
10946 var changed = input.poll();
10947 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
10948 else {input.pollingFast = false; input.slowPoll();}
10949 }
10950 input.polling.set(20, p);
10951 };
10952
10953 // Read input from the textarea, and update the document to match.
10954 // When something is selected, it is present in the textarea, and
10955 // selected (unless it is huge, in which case a placeholder is
10956 // used). When nothing is selected, the cursor sits after previously
10957 // seen text (can be empty), which is stored in prevInput (we must
10958 // not reset the textarea when typing, because that breaks IME).
10959 TextareaInput.prototype.poll = function () {
10960 var this$1 = this;
10961
10962 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
10963 // Since this is called a *lot*, try to bail out as cheaply as
10964 // possible when it is clear that nothing happened. hasSelection
10965 // will be the case when there is a lot of text in the textarea,
10966 // in which case reading its value would be expensive.
10967 if (this.contextMenuPending || !cm.state.focused ||
10968 (hasSelection(input) && !prevInput && !this.composing) ||
10969 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
10970 { return false }
10971
10972 var text = input.value;
10973 // If nothing changed, bail.
10974 if (text == prevInput && !cm.somethingSelected()) { return false }
10975 // Work around nonsensical selection resetting in IE9/10, and
10976 // inexplicable appearance of private area unicode characters on
10977 // some key combos in Mac (#2689).
10978 if (ie && ie_version >= 9 && this.hasSelection === text ||
10979 mac && /[\uf700-\uf7ff]/.test(text)) {
10980 cm.display.input.reset();
10981 return false
10982 }
10983
10984 if (cm.doc.sel == cm.display.selForContextMenu) {
10985 var first = text.charCodeAt(0);
10986 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
10987 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
10988 }
10989 // Find the part of the input that is actually new
10990 var same = 0, l = Math.min(prevInput.length, text.length);
10991 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
10992
10993 runInOp(cm, function () {
10994 applyTextInput(cm, text.slice(same), prevInput.length - same,
10995 null, this$1.composing ? "*compose" : null);
10996
10997 // Don't leave long text in the textarea, since it makes further polling slow
10998 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
10999 else { this$1.prevInput = text; }
11000
11001 if (this$1.composing) {
11002 this$1.composing.range.clear();
11003 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
11004 {className: "CodeMirror-composing"});
11005 }
11006 });
11007 return true
11008 };
11009
11010 TextareaInput.prototype.ensurePolled = function () {
11011 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
11012 };
11013
11014 TextareaInput.prototype.onKeyPress = function () {
11015 if (ie && ie_version >= 9) { this.hasSelection = null; }
11016 this.fastPoll();
11017 };
11018
11019 TextareaInput.prototype.onContextMenu = function (e) {
11020 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
11021 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
11022 if (!pos || presto) { return } // Opera is difficult.
11023
11024 // Reset the current text selection only if the click is done outside of the selection
11025 // and 'resetSelectionOnContextMenu' option is true.
11026 var reset = cm.options.resetSelectionOnContextMenu;
11027 if (reset && cm.doc.sel.contains(pos) == -1)
11028 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
11029
11030 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
11031 input.wrapper.style.cssText = "position: absolute";
11032 var wrapperBox = input.wrapper.getBoundingClientRect();
11033 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);";
11034 var oldScrollY;
11035 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
11036 display.input.focus();
11037 if (webkit) { window.scrollTo(null, oldScrollY); }
11038 display.input.reset();
11039 // Adds "Select all" to context menu in FF
11040 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
11041 input.contextMenuPending = true;
11042 display.selForContextMenu = cm.doc.sel;
11043 clearTimeout(display.detectingSelectAll);
11044
11045 // Select-all will be greyed out if there's nothing to select, so
11046 // this adds a zero-width space so that we can later check whether
11047 // it got selected.
11048 function prepareSelectAllHack() {
11049 if (te.selectionStart != null) {
11050 var selected = cm.somethingSelected();
11051 var extval = "\u200b" + (selected ? te.value : "");
11052 te.value = "\u21da"; // Used to catch context-menu undo
11053 te.value = extval;
11054 input.prevInput = selected ? "" : "\u200b";
11055 te.selectionStart = 1; te.selectionEnd = extval.length;
11056 // Re-set this, in case some other handler touched the
11057 // selection in the meantime.
11058 display.selForContextMenu = cm.doc.sel;
11059 }
11060 }
11061 function rehide() {
11062 input.contextMenuPending = false;
11063 input.wrapper.style.cssText = oldWrapperCSS;
11064 te.style.cssText = oldCSS;
11065 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
11066
11067 // Try to detect the user choosing select-all
11068 if (te.selectionStart != null) {
11069 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
11070 var i = 0, poll = function () {
11071 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
11072 te.selectionEnd > 0 && input.prevInput == "\u200b") {
11073 operation(cm, selectAll)(cm);
11074 } else if (i++ < 10) {
11075 display.detectingSelectAll = setTimeout(poll, 500);
11076 } else {
11077 display.selForContextMenu = null;
11078 display.input.reset();
11079 }
11080 };
11081 display.detectingSelectAll = setTimeout(poll, 200);
11082 }
11083 }
11084
11085 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
11086 if (captureRightClick) {
11087 e_stop(e);
11088 var mouseup = function () {
11089 off(window, "mouseup", mouseup);
11090 setTimeout(rehide, 20);
11091 };
11092 on(window, "mouseup", mouseup);
11093 } else {
11094 setTimeout(rehide, 50);
11095 }
11096 };
11097
11098 TextareaInput.prototype.readOnlyChanged = function (val) {
11099 if (!val) { this.reset(); }
11100 this.textarea.disabled = val == "nocursor";
11101 };
11102
11103 TextareaInput.prototype.setUneditable = function () {};
11104
11105 TextareaInput.prototype.needsContentAttribute = false;
11106
11107 function fromTextArea(textarea, options) {
11108 options = options ? copyObj(options) : {};
11109 options.value = textarea.value;
11110 if (!options.tabindex && textarea.tabIndex)
11111 { options.tabindex = textarea.tabIndex; }
11112 if (!options.placeholder && textarea.placeholder)
11113 { options.placeholder = textarea.placeholder; }
11114 // Set autofocus to true if this textarea is focused, or if it has
11115 // autofocus and no other element is focused.
11116 if (options.autofocus == null) {
11117 var hasFocus = activeElt();
11118 options.autofocus = hasFocus == textarea ||
11119 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
11120 }
11121
11122 function save() {textarea.value = cm.getValue();}
11123
11124 var realSubmit;
11125 if (textarea.form) {
11126 on(textarea.form, "submit", save);
11127 // Deplorable hack to make the submit method do the right thing.
11128 if (!options.leaveSubmitMethodAlone) {
11129 var form = textarea.form;
11130 realSubmit = form.submit;
11131 try {
11132 var wrappedSubmit = form.submit = function () {
11133 save();
11134 form.submit = realSubmit;
11135 form.submit();
11136 form.submit = wrappedSubmit;
11137 };
11138 } catch(e) {}
11139 }
11140 }
11141
11142 options.finishInit = function (cm) {
11143 cm.save = save;
11144 cm.getTextArea = function () { return textarea; };
11145 cm.toTextArea = function () {
11146 cm.toTextArea = isNaN; // Prevent this from being ran twice
11147 save();
11148 textarea.parentNode.removeChild(cm.getWrapperElement());
11149 textarea.style.display = "";
11150 if (textarea.form) {
11151 off(textarea.form, "submit", save);
11152 if (typeof textarea.form.submit == "function")
11153 { textarea.form.submit = realSubmit; }
11154 }
11155 };
11156 };
11157
11158 textarea.style.display = "none";
11159 var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11160 options);
11161 return cm
11162 }
11163
11164 function addLegacyProps(CodeMirror) {
11165 CodeMirror.off = off;
11166 CodeMirror.on = on;
11167 CodeMirror.wheelEventPixels = wheelEventPixels;
11168 CodeMirror.Doc = Doc;
11169 CodeMirror.splitLines = splitLinesAuto;
11170 CodeMirror.countColumn = countColumn;
11171 CodeMirror.findColumn = findColumn;
11172 CodeMirror.isWordChar = isWordCharBasic;
11173 CodeMirror.Pass = Pass;
11174 CodeMirror.signal = signal;
11175 CodeMirror.Line = Line;
11176 CodeMirror.changeEnd = changeEnd;
11177 CodeMirror.scrollbarModel = scrollbarModel;
11178 CodeMirror.Pos = Pos;
11179 CodeMirror.cmpPos = cmp;
11180 CodeMirror.modes = modes;
11181 CodeMirror.mimeModes = mimeModes;
11182 CodeMirror.resolveMode = resolveMode;
11183 CodeMirror.getMode = getMode;
11184 CodeMirror.modeExtensions = modeExtensions;
11185 CodeMirror.extendMode = extendMode;
11186 CodeMirror.copyState = copyState;
11187 CodeMirror.startState = startState;
11188 CodeMirror.innerMode = innerMode;
11189 CodeMirror.commands = commands;
11190 CodeMirror.keyMap = keyMap;
11191 CodeMirror.keyName = keyName;
11192 CodeMirror.isModifierKey = isModifierKey;
11193 CodeMirror.lookupKey = lookupKey;
11194 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11195 CodeMirror.StringStream = StringStream;
11196 CodeMirror.SharedTextMarker = SharedTextMarker;
11197 CodeMirror.TextMarker = TextMarker;
11198 CodeMirror.LineWidget = LineWidget;
11199 CodeMirror.e_preventDefault = e_preventDefault;
11200 CodeMirror.e_stopPropagation = e_stopPropagation;
11201 CodeMirror.e_stop = e_stop;
11202 CodeMirror.addClass = addClass;
11203 CodeMirror.contains = contains;
11204 CodeMirror.rmClass = rmClass;
11205 CodeMirror.keyNames = keyNames;
11206 }
11207
11208 // EDITOR CONSTRUCTOR
11209
11210 defineOptions(CodeMirror$1);
11211
11212 addEditorMethods(CodeMirror$1);
11213
11214 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11215 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11216 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11217 { CodeMirror$1.prototype[prop] = (function(method) {
11218 return function() {return method.apply(this.doc, arguments)}
11219 })(Doc.prototype[prop]); } }
11220
11221 eventMixin(Doc);
11222
11223 // INPUT HANDLING
11224
11225 CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11226
11227 // MODE DEFINITION AND QUERYING
11228
11229 // Extra arguments are stored as the mode's dependencies, which is
11230 // used by (legacy) mechanisms like loadmode.js to automatically
11231 // load a mode. (Preferred mechanism is the require/define calls.)
11232 CodeMirror$1.defineMode = function(name/*, mode, …*/) {
11233 if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; }
11234 defineMode.apply(this, arguments);
11235 };
11236
11237 CodeMirror$1.defineMIME = defineMIME;
11238
11239 // Minimal default mode.
11240 CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11241 CodeMirror$1.defineMIME("text/plain", "null");
11242
11243 // EXTENSIONS
11244
11245 CodeMirror$1.defineExtension = function (name, func) {
11246 CodeMirror$1.prototype[name] = func;
11247 };
11248 CodeMirror$1.defineDocExtension = function (name, func) {
11249 Doc.prototype[name] = func;
11250 };
11251
11252 CodeMirror$1.fromTextArea = fromTextArea;
11253
11254 addLegacyProps(CodeMirror$1);
11255
11256 CodeMirror$1.version = "5.34.0";
11257
11258 return CodeMirror$1;
11259
11260 })));
11261
11262 },{}],15:[function(require,module,exports){
11263 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11264 // Distributed under an MIT license: http://codemirror.net/LICENSE
11265
11266 (function(mod) {
11267 if (typeof exports == "object" && typeof module == "object") // CommonJS
11268 mod(require("../../lib/codemirror"));
11269 else if (typeof define == "function" && define.amd) // AMD
11270 define(["../../lib/codemirror"], mod);
11271 else // Plain browser env
11272 mod(CodeMirror);
11273 })(function(CodeMirror) {
11274 "use strict";
11275
11276 CodeMirror.defineMode("css", function(config, parserConfig) {
11277 var inline = parserConfig.inline
11278 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11279
11280 var indentUnit = config.indentUnit,
11281 tokenHooks = parserConfig.tokenHooks,
11282 documentTypes = parserConfig.documentTypes || {},
11283 mediaTypes = parserConfig.mediaTypes || {},
11284 mediaFeatures = parserConfig.mediaFeatures || {},
11285 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11286 propertyKeywords = parserConfig.propertyKeywords || {},
11287 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11288 fontProperties = parserConfig.fontProperties || {},
11289 counterDescriptors = parserConfig.counterDescriptors || {},
11290 colorKeywords = parserConfig.colorKeywords || {},
11291 valueKeywords = parserConfig.valueKeywords || {},
11292 allowNested = parserConfig.allowNested,
11293 lineComment = parserConfig.lineComment,
11294 supportsAtComponent = parserConfig.supportsAtComponent === true;
11295
11296 var type, override;
11297 function ret(style, tp) { type = tp; return style; }
11298
11299 // Tokenizers
11300
11301 function tokenBase(stream, state) {
11302 var ch = stream.next();
11303 if (tokenHooks[ch]) {
11304 var result = tokenHooks[ch](stream, state);
11305 if (result !== false) return result;
11306 }
11307 if (ch == "@") {
11308 stream.eatWhile(/[\w\\\-]/);
11309 return ret("def", stream.current());
11310 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11311 return ret(null, "compare");
11312 } else if (ch == "\"" || ch == "'") {
11313 state.tokenize = tokenString(ch);
11314 return state.tokenize(stream, state);
11315 } else if (ch == "#") {
11316 stream.eatWhile(/[\w\\\-]/);
11317 return ret("atom", "hash");
11318 } else if (ch == "!") {
11319 stream.match(/^\s*\w*/);
11320 return ret("keyword", "important");
11321 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11322 stream.eatWhile(/[\w.%]/);
11323 return ret("number", "unit");
11324 } else if (ch === "-") {
11325 if (/[\d.]/.test(stream.peek())) {
11326 stream.eatWhile(/[\w.%]/);
11327 return ret("number", "unit");
11328 } else if (stream.match(/^-[\w\\\-]+/)) {
11329 stream.eatWhile(/[\w\\\-]/);
11330 if (stream.match(/^\s*:/, false))
11331 return ret("variable-2", "variable-definition");
11332 return ret("variable-2", "variable");
11333 } else if (stream.match(/^\w+-/)) {
11334 return ret("meta", "meta");
11335 }
11336 } else if (/[,+>*\/]/.test(ch)) {
11337 return ret(null, "select-op");
11338 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11339 return ret("qualifier", "qualifier");
11340 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11341 return ret(null, ch);
11342 } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
11343 ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
11344 ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
11345 stream.backUp(1);
11346 state.tokenize = tokenParenthesized;
11347 return ret("property", "word");
11348 } else if (/[\w\\\-]/.test(ch)) {
11349 stream.eatWhile(/[\w\\\-]/);
11350 return ret("property", "word");
11351 } else {
11352 return ret(null, null);
11353 }
11354 }
11355
11356 function tokenString(quote) {
11357 return function(stream, state) {
11358 var escaped = false, ch;
11359 while ((ch = stream.next()) != null) {
11360 if (ch == quote && !escaped) {
11361 if (quote == ")") stream.backUp(1);
11362 break;
11363 }
11364 escaped = !escaped && ch == "\\";
11365 }
11366 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11367 return ret("string", "string");
11368 };
11369 }
11370
11371 function tokenParenthesized(stream, state) {
11372 stream.next(); // Must be '('
11373 if (!stream.match(/\s*[\"\')]/, false))
11374 state.tokenize = tokenString(")");
11375 else
11376 state.tokenize = null;
11377 return ret(null, "(");
11378 }
11379
11380 // Context management
11381
11382 function Context(type, indent, prev) {
11383 this.type = type;
11384 this.indent = indent;
11385 this.prev = prev;
11386 }
11387
11388 function pushContext(state, stream, type, indent) {
11389 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11390 return type;
11391 }
11392
11393 function popContext(state) {
11394 if (state.context.prev)
11395 state.context = state.context.prev;
11396 return state.context.type;
11397 }
11398
11399 function pass(type, stream, state) {
11400 return states[state.context.type](type, stream, state);
11401 }
11402 function popAndPass(type, stream, state, n) {
11403 for (var i = n || 1; i > 0; i--)
11404 state.context = state.context.prev;
11405 return pass(type, stream, state);
11406 }
11407
11408 // Parser
11409
11410 function wordAsValue(stream) {
11411 var word = stream.current().toLowerCase();
11412 if (valueKeywords.hasOwnProperty(word))
11413 override = "atom";
11414 else if (colorKeywords.hasOwnProperty(word))
11415 override = "keyword";
11416 else
11417 override = "variable";
11418 }
11419
11420 var states = {};
11421
11422 states.top = function(type, stream, state) {
11423 if (type == "{") {
11424 return pushContext(state, stream, "block");
11425 } else if (type == "}" && state.context.prev) {
11426 return popContext(state);
11427 } else if (supportsAtComponent && /@component/i.test(type)) {
11428 return pushContext(state, stream, "atComponentBlock");
11429 } else if (/^@(-moz-)?document$/i.test(type)) {
11430 return pushContext(state, stream, "documentTypes");
11431 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11432 return pushContext(state, stream, "atBlock");
11433 } else if (/^@(font-face|counter-style)/i.test(type)) {
11434 state.stateArg = type;
11435 return "restricted_atBlock_before";
11436 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11437 return "keyframes";
11438 } else if (type && type.charAt(0) == "@") {
11439 return pushContext(state, stream, "at");
11440 } else if (type == "hash") {
11441 override = "builtin";
11442 } else if (type == "word") {
11443 override = "tag";
11444 } else if (type == "variable-definition") {
11445 return "maybeprop";
11446 } else if (type == "interpolation") {
11447 return pushContext(state, stream, "interpolation");
11448 } else if (type == ":") {
11449 return "pseudo";
11450 } else if (allowNested && type == "(") {
11451 return pushContext(state, stream, "parens");
11452 }
11453 return state.context.type;
11454 };
11455
11456 states.block = function(type, stream, state) {
11457 if (type == "word") {
11458 var word = stream.current().toLowerCase();
11459 if (propertyKeywords.hasOwnProperty(word)) {
11460 override = "property";
11461 return "maybeprop";
11462 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11463 override = "string-2";
11464 return "maybeprop";
11465 } else if (allowNested) {
11466 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11467 return "block";
11468 } else {
11469 override += " error";
11470 return "maybeprop";
11471 }
11472 } else if (type == "meta") {
11473 return "block";
11474 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11475 override = "error";
11476 return "block";
11477 } else {
11478 return states.top(type, stream, state);
11479 }
11480 };
11481
11482 states.maybeprop = function(type, stream, state) {
11483 if (type == ":") return pushContext(state, stream, "prop");
11484 return pass(type, stream, state);
11485 };
11486
11487 states.prop = function(type, stream, state) {
11488 if (type == ";") return popContext(state);
11489 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11490 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11491 if (type == "(") return pushContext(state, stream, "parens");
11492
11493 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11494 override += " error";
11495 } else if (type == "word") {
11496 wordAsValue(stream);
11497 } else if (type == "interpolation") {
11498 return pushContext(state, stream, "interpolation");
11499 }
11500 return "prop";
11501 };
11502
11503 states.propBlock = function(type, _stream, state) {
11504 if (type == "}") return popContext(state);
11505 if (type == "word") { override = "property"; return "maybeprop"; }
11506 return state.context.type;
11507 };
11508
11509 states.parens = function(type, stream, state) {
11510 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11511 if (type == ")") return popContext(state);
11512 if (type == "(") return pushContext(state, stream, "parens");
11513 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11514 if (type == "word") wordAsValue(stream);
11515 return "parens";
11516 };
11517
11518 states.pseudo = function(type, stream, state) {
11519 if (type == "meta") return "pseudo";
11520
11521 if (type == "word") {
11522 override = "variable-3";
11523 return state.context.type;
11524 }
11525 return pass(type, stream, state);
11526 };
11527
11528 states.documentTypes = function(type, stream, state) {
11529 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11530 override = "tag";
11531 return state.context.type;
11532 } else {
11533 return states.atBlock(type, stream, state);
11534 }
11535 };
11536
11537 states.atBlock = function(type, stream, state) {
11538 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11539 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11540 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11541
11542 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11543
11544 if (type == "word") {
11545 var word = stream.current().toLowerCase();
11546 if (word == "only" || word == "not" || word == "and" || word == "or")
11547 override = "keyword";
11548 else if (mediaTypes.hasOwnProperty(word))
11549 override = "attribute";
11550 else if (mediaFeatures.hasOwnProperty(word))
11551 override = "property";
11552 else if (mediaValueKeywords.hasOwnProperty(word))
11553 override = "keyword";
11554 else if (propertyKeywords.hasOwnProperty(word))
11555 override = "property";
11556 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11557 override = "string-2";
11558 else if (valueKeywords.hasOwnProperty(word))
11559 override = "atom";
11560 else if (colorKeywords.hasOwnProperty(word))
11561 override = "keyword";
11562 else
11563 override = "error";
11564 }
11565 return state.context.type;
11566 };
11567
11568 states.atComponentBlock = function(type, stream, state) {
11569 if (type == "}")
11570 return popAndPass(type, stream, state);
11571 if (type == "{")
11572 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11573 if (type == "word")
11574 override = "error";
11575 return state.context.type;
11576 };
11577
11578 states.atBlock_parens = function(type, stream, state) {
11579 if (type == ")") return popContext(state);
11580 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11581 return states.atBlock(type, stream, state);
11582 };
11583
11584 states.restricted_atBlock_before = function(type, stream, state) {
11585 if (type == "{")
11586 return pushContext(state, stream, "restricted_atBlock");
11587 if (type == "word" && state.stateArg == "@counter-style") {
11588 override = "variable";
11589 return "restricted_atBlock_before";
11590 }
11591 return pass(type, stream, state);
11592 };
11593
11594 states.restricted_atBlock = function(type, stream, state) {
11595 if (type == "}") {
11596 state.stateArg = null;
11597 return popContext(state);
11598 }
11599 if (type == "word") {
11600 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11601 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11602 override = "error";
11603 else
11604 override = "property";
11605 return "maybeprop";
11606 }
11607 return "restricted_atBlock";
11608 };
11609
11610 states.keyframes = function(type, stream, state) {
11611 if (type == "word") { override = "variable"; return "keyframes"; }
11612 if (type == "{") return pushContext(state, stream, "top");
11613 return pass(type, stream, state);
11614 };
11615
11616 states.at = function(type, stream, state) {
11617 if (type == ";") return popContext(state);
11618 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11619 if (type == "word") override = "tag";
11620 else if (type == "hash") override = "builtin";
11621 return "at";
11622 };
11623
11624 states.interpolation = function(type, stream, state) {
11625 if (type == "}") return popContext(state);
11626 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11627 if (type == "word") override = "variable";
11628 else if (type != "variable" && type != "(" && type != ")") override = "error";
11629 return "interpolation";
11630 };
11631
11632 return {
11633 startState: function(base) {
11634 return {tokenize: null,
11635 state: inline ? "block" : "top",
11636 stateArg: null,
11637 context: new Context(inline ? "block" : "top", base || 0, null)};
11638 },
11639
11640 token: function(stream, state) {
11641 if (!state.tokenize && stream.eatSpace()) return null;
11642 var style = (state.tokenize || tokenBase)(stream, state);
11643 if (style && typeof style == "object") {
11644 type = style[1];
11645 style = style[0];
11646 }
11647 override = style;
11648 if (type != "comment")
11649 state.state = states[state.state](type, stream, state);
11650 return override;
11651 },
11652
11653 indent: function(state, textAfter) {
11654 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11655 var indent = cx.indent;
11656 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11657 if (cx.prev) {
11658 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11659 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11660 // Resume indentation from parent context.
11661 cx = cx.prev;
11662 indent = cx.indent;
11663 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11664 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11665 // Dedent relative to current context.
11666 indent = Math.max(0, cx.indent - indentUnit);
11667 }
11668 }
11669 return indent;
11670 },
11671
11672 electricChars: "}",
11673 blockCommentStart: "/*",
11674 blockCommentEnd: "*/",
11675 blockCommentContinue: " * ",
11676 lineComment: lineComment,
11677 fold: "brace"
11678 };
11679 });
11680
11681 function keySet(array) {
11682 var keys = {};
11683 for (var i = 0; i < array.length; ++i) {
11684 keys[array[i].toLowerCase()] = true;
11685 }
11686 return keys;
11687 }
11688
11689 var documentTypes_ = [
11690 "domain", "regexp", "url", "url-prefix"
11691 ], documentTypes = keySet(documentTypes_);
11692
11693 var mediaTypes_ = [
11694 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11695 "tty", "tv", "embossed"
11696 ], mediaTypes = keySet(mediaTypes_);
11697
11698 var mediaFeatures_ = [
11699 "width", "min-width", "max-width", "height", "min-height", "max-height",
11700 "device-width", "min-device-width", "max-device-width", "device-height",
11701 "min-device-height", "max-device-height", "aspect-ratio",
11702 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11703 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11704 "max-color", "color-index", "min-color-index", "max-color-index",
11705 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11706 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11707 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11708 "pointer", "any-pointer", "hover", "any-hover"
11709 ], mediaFeatures = keySet(mediaFeatures_);
11710
11711 var mediaValueKeywords_ = [
11712 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11713 "interlace", "progressive"
11714 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11715
11716 var propertyKeywords_ = [
11717 "align-content", "align-items", "align-self", "alignment-adjust",
11718 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11719 "animation-direction", "animation-duration", "animation-fill-mode",
11720 "animation-iteration-count", "animation-name", "animation-play-state",
11721 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11722 "background", "background-attachment", "background-blend-mode", "background-clip",
11723 "background-color", "background-image", "background-origin", "background-position",
11724 "background-repeat", "background-size", "baseline-shift", "binding",
11725 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11726 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11727 "border-bottom-left-radius", "border-bottom-right-radius",
11728 "border-bottom-style", "border-bottom-width", "border-collapse",
11729 "border-color", "border-image", "border-image-outset",
11730 "border-image-repeat", "border-image-slice", "border-image-source",
11731 "border-image-width", "border-left", "border-left-color",
11732 "border-left-style", "border-left-width", "border-radius", "border-right",
11733 "border-right-color", "border-right-style", "border-right-width",
11734 "border-spacing", "border-style", "border-top", "border-top-color",
11735 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11736 "border-top-width", "border-width", "bottom", "box-decoration-break",
11737 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11738 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11739 "column-fill", "column-gap", "column-rule", "column-rule-color",
11740 "column-rule-style", "column-rule-width", "column-span", "column-width",
11741 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11742 "cue-after", "cue-before", "cursor", "direction", "display",
11743 "dominant-baseline", "drop-initial-after-adjust",
11744 "drop-initial-after-align", "drop-initial-before-adjust",
11745 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11746 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11747 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11748 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11749 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11750 "font-stretch", "font-style", "font-synthesis", "font-variant",
11751 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11752 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11753 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11754 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11755 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11756 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11757 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11758 "icon", "image-orientation", "image-rendering", "image-resolution",
11759 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11760 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11761 "line-stacking-shift", "line-stacking-strategy", "list-style",
11762 "list-style-image", "list-style-position", "list-style-type", "margin",
11763 "margin-bottom", "margin-left", "margin-right", "margin-top",
11764 "marks", "marquee-direction", "marquee-loop",
11765 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11766 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
11767 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11768 "opacity", "order", "orphans", "outline",
11769 "outline-color", "outline-offset", "outline-style", "outline-width",
11770 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11771 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11772 "page", "page-break-after", "page-break-before", "page-break-inside",
11773 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11774 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11775 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11776 "region-break-before", "region-break-inside", "region-fragment",
11777 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11778 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11779 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11780 "shape-outside", "size", "speak", "speak-as", "speak-header",
11781 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11782 "tab-size", "table-layout", "target", "target-name", "target-new",
11783 "target-position", "text-align", "text-align-last", "text-decoration",
11784 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11785 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11786 "text-emphasis-position", "text-emphasis-style", "text-height",
11787 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11788 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11789 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11790 "transition", "transition-delay", "transition-duration",
11791 "transition-property", "transition-timing-function", "unicode-bidi",
11792 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11793 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11794 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11795 "word-spacing", "word-wrap", "z-index",
11796 // SVG-specific
11797 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11798 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11799 "color-interpolation", "color-interpolation-filters",
11800 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11801 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11802 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11803 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11804 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11805 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11806 ], propertyKeywords = keySet(propertyKeywords_);
11807
11808 var nonStandardPropertyKeywords_ = [
11809 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11810 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11811 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11812 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11813 "searchfield-results-decoration", "zoom"
11814 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11815
11816 var fontProperties_ = [
11817 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11818 "font-stretch", "font-weight", "font-style"
11819 ], fontProperties = keySet(fontProperties_);
11820
11821 var counterDescriptors_ = [
11822 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11823 "speak-as", "suffix", "symbols", "system"
11824 ], counterDescriptors = keySet(counterDescriptors_);
11825
11826 var colorKeywords_ = [
11827 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11828 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11829 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11830 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11831 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11832 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11833 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11834 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11835 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11836 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11837 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11838 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11839 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11840 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11841 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11842 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11843 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11844 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11845 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11846 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11847 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11848 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11849 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11850 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11851 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11852 "whitesmoke", "yellow", "yellowgreen"
11853 ], colorKeywords = keySet(colorKeywords_);
11854
11855 var valueKeywords_ = [
11856 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11857 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11858 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11859 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11860 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11861 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11862 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11863 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11864 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11865 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11866 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11867 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11868 "compact", "condensed", "contain", "content", "contents",
11869 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11870 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11871 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11872 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11873 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11874 "dot-dash", "dot-dot-dash",
11875 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11876 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11877 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11878 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11879 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11880 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11881 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11882 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11883 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11884 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11885 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11886 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11887 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11888 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11889 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11890 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11891 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11892 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11893 "katakana", "katakana-iroha", "keep-all", "khmer",
11894 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11895 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11896 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11897 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11898 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11899 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11900 "media-controls-background", "media-current-time-display",
11901 "media-fullscreen-button", "media-mute-button", "media-play-button",
11902 "media-return-to-realtime-button", "media-rewind-button",
11903 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11904 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11905 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11906 "menu", "menulist", "menulist-button", "menulist-text",
11907 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11908 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11909 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11910 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11911 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11912 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11913 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11914 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11915 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11916 "progress", "push-button", "radial-gradient", "radio", "read-only",
11917 "read-write", "read-write-plaintext-only", "rectangle", "region",
11918 "relative", "repeat", "repeating-linear-gradient",
11919 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
11920 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
11921 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
11922 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
11923 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
11924 "searchfield-cancel-button", "searchfield-decoration",
11925 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
11926 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
11927 "simp-chinese-formal", "simp-chinese-informal", "single",
11928 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
11929 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
11930 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
11931 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
11932 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
11933 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
11934 "table-caption", "table-cell", "table-column", "table-column-group",
11935 "table-footer-group", "table-header-group", "table-row", "table-row-group",
11936 "tamil",
11937 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
11938 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
11939 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
11940 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
11941 "trad-chinese-formal", "trad-chinese-informal", "transform",
11942 "translate", "translate3d", "translateX", "translateY", "translateZ",
11943 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
11944 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
11945 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
11946 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
11947 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
11948 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
11949 "xx-large", "xx-small"
11950 ], valueKeywords = keySet(valueKeywords_);
11951
11952 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
11953 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
11954 .concat(valueKeywords_);
11955 CodeMirror.registerHelper("hintWords", "css", allWords);
11956
11957 function tokenCComment(stream, state) {
11958 var maybeEnd = false, ch;
11959 while ((ch = stream.next()) != null) {
11960 if (maybeEnd && ch == "/") {
11961 state.tokenize = null;
11962 break;
11963 }
11964 maybeEnd = (ch == "*");
11965 }
11966 return ["comment", "comment"];
11967 }
11968
11969 CodeMirror.defineMIME("text/css", {
11970 documentTypes: documentTypes,
11971 mediaTypes: mediaTypes,
11972 mediaFeatures: mediaFeatures,
11973 mediaValueKeywords: mediaValueKeywords,
11974 propertyKeywords: propertyKeywords,
11975 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11976 fontProperties: fontProperties,
11977 counterDescriptors: counterDescriptors,
11978 colorKeywords: colorKeywords,
11979 valueKeywords: valueKeywords,
11980 tokenHooks: {
11981 "/": function(stream, state) {
11982 if (!stream.eat("*")) return false;
11983 state.tokenize = tokenCComment;
11984 return tokenCComment(stream, state);
11985 }
11986 },
11987 name: "css"
11988 });
11989
11990 CodeMirror.defineMIME("text/x-scss", {
11991 mediaTypes: mediaTypes,
11992 mediaFeatures: mediaFeatures,
11993 mediaValueKeywords: mediaValueKeywords,
11994 propertyKeywords: propertyKeywords,
11995 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
11996 colorKeywords: colorKeywords,
11997 valueKeywords: valueKeywords,
11998 fontProperties: fontProperties,
11999 allowNested: true,
12000 lineComment: "//",
12001 tokenHooks: {
12002 "/": function(stream, state) {
12003 if (stream.eat("/")) {
12004 stream.skipToEnd();
12005 return ["comment", "comment"];
12006 } else if (stream.eat("*")) {
12007 state.tokenize = tokenCComment;
12008 return tokenCComment(stream, state);
12009 } else {
12010 return ["operator", "operator"];
12011 }
12012 },
12013 ":": function(stream) {
12014 if (stream.match(/\s*\{/, false))
12015 return [null, null]
12016 return false;
12017 },
12018 "$": function(stream) {
12019 stream.match(/^[\w-]+/);
12020 if (stream.match(/^\s*:/, false))
12021 return ["variable-2", "variable-definition"];
12022 return ["variable-2", "variable"];
12023 },
12024 "#": function(stream) {
12025 if (!stream.eat("{")) return false;
12026 return [null, "interpolation"];
12027 }
12028 },
12029 name: "css",
12030 helperType: "scss"
12031 });
12032
12033 CodeMirror.defineMIME("text/x-less", {
12034 mediaTypes: mediaTypes,
12035 mediaFeatures: mediaFeatures,
12036 mediaValueKeywords: mediaValueKeywords,
12037 propertyKeywords: propertyKeywords,
12038 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12039 colorKeywords: colorKeywords,
12040 valueKeywords: valueKeywords,
12041 fontProperties: fontProperties,
12042 allowNested: true,
12043 lineComment: "//",
12044 tokenHooks: {
12045 "/": function(stream, state) {
12046 if (stream.eat("/")) {
12047 stream.skipToEnd();
12048 return ["comment", "comment"];
12049 } else if (stream.eat("*")) {
12050 state.tokenize = tokenCComment;
12051 return tokenCComment(stream, state);
12052 } else {
12053 return ["operator", "operator"];
12054 }
12055 },
12056 "@": function(stream) {
12057 if (stream.eat("{")) return [null, "interpolation"];
12058 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12059 stream.eatWhile(/[\w\\\-]/);
12060 if (stream.match(/^\s*:/, false))
12061 return ["variable-2", "variable-definition"];
12062 return ["variable-2", "variable"];
12063 },
12064 "&": function() {
12065 return ["atom", "atom"];
12066 }
12067 },
12068 name: "css",
12069 helperType: "less"
12070 });
12071
12072 CodeMirror.defineMIME("text/x-gss", {
12073 documentTypes: documentTypes,
12074 mediaTypes: mediaTypes,
12075 mediaFeatures: mediaFeatures,
12076 propertyKeywords: propertyKeywords,
12077 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12078 fontProperties: fontProperties,
12079 counterDescriptors: counterDescriptors,
12080 colorKeywords: colorKeywords,
12081 valueKeywords: valueKeywords,
12082 supportsAtComponent: true,
12083 tokenHooks: {
12084 "/": function(stream, state) {
12085 if (!stream.eat("*")) return false;
12086 state.tokenize = tokenCComment;
12087 return tokenCComment(stream, state);
12088 }
12089 },
12090 name: "css",
12091 helperType: "gss"
12092 });
12093
12094 });
12095
12096 },{"../../lib/codemirror":14}],16:[function(require,module,exports){
12097 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12098 // Distributed under an MIT license: http://codemirror.net/LICENSE
12099
12100 (function(mod) {
12101 if (typeof exports == "object" && typeof module == "object") // CommonJS
12102 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
12103 else if (typeof define == "function" && define.amd) // AMD
12104 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
12105 else // Plain browser env
12106 mod(CodeMirror);
12107 })(function(CodeMirror) {
12108 "use strict";
12109
12110 var defaultTags = {
12111 script: [
12112 ["lang", /(javascript|babel)/i, "javascript"],
12113 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
12114 ["type", /./, "text/plain"],
12115 [null, null, "javascript"]
12116 ],
12117 style: [
12118 ["lang", /^css$/i, "css"],
12119 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
12120 ["type", /./, "text/plain"],
12121 [null, null, "css"]
12122 ]
12123 };
12124
12125 function maybeBackup(stream, pat, style) {
12126 var cur = stream.current(), close = cur.search(pat);
12127 if (close > -1) {
12128 stream.backUp(cur.length - close);
12129 } else if (cur.match(/<\/?$/)) {
12130 stream.backUp(cur.length);
12131 if (!stream.match(pat, false)) stream.match(cur);
12132 }
12133 return style;
12134 }
12135
12136 var attrRegexpCache = {};
12137 function getAttrRegexp(attr) {
12138 var regexp = attrRegexpCache[attr];
12139 if (regexp) return regexp;
12140 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12141 }
12142
12143 function getAttrValue(text, attr) {
12144 var match = text.match(getAttrRegexp(attr))
12145 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12146 }
12147
12148 function getTagRegexp(tagName, anchored) {
12149 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12150 }
12151
12152 function addTags(from, to) {
12153 for (var tag in from) {
12154 var dest = to[tag] || (to[tag] = []);
12155 var source = from[tag];
12156 for (var i = source.length - 1; i >= 0; i--)
12157 dest.unshift(source[i])
12158 }
12159 }
12160
12161 function findMatchingMode(tagInfo, tagText) {
12162 for (var i = 0; i < tagInfo.length; i++) {
12163 var spec = tagInfo[i];
12164 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12165 }
12166 }
12167
12168 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12169 var htmlMode = CodeMirror.getMode(config, {
12170 name: "xml",
12171 htmlMode: true,
12172 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12173 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12174 });
12175
12176 var tags = {};
12177 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12178 addTags(defaultTags, tags);
12179 if (configTags) addTags(configTags, tags);
12180 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12181 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12182
12183 function html(stream, state) {
12184 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12185 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12186 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12187 tags.hasOwnProperty(tagName)) {
12188 state.inTag = tagName + " "
12189 } else if (state.inTag && tag && />$/.test(stream.current())) {
12190 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12191 state.inTag = null
12192 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12193 var mode = CodeMirror.getMode(config, modeSpec)
12194 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12195 state.token = function (stream, state) {
12196 if (stream.match(endTagA, false)) {
12197 state.token = html;
12198 state.localState = state.localMode = null;
12199 return null;
12200 }
12201 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12202 };
12203 state.localMode = mode;
12204 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
12205 } else if (state.inTag) {
12206 state.inTag += stream.current()
12207 if (stream.eol()) state.inTag += " "
12208 }
12209 return style;
12210 };
12211
12212 return {
12213 startState: function () {
12214 var state = CodeMirror.startState(htmlMode);
12215 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12216 },
12217
12218 copyState: function (state) {
12219 var local;
12220 if (state.localState) {
12221 local = CodeMirror.copyState(state.localMode, state.localState);
12222 }
12223 return {token: state.token, inTag: state.inTag,
12224 localMode: state.localMode, localState: local,
12225 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12226 },
12227
12228 token: function (stream, state) {
12229 return state.token(stream, state);
12230 },
12231
12232 indent: function (state, textAfter, line) {
12233 if (!state.localMode || /^\s*<\//.test(textAfter))
12234 return htmlMode.indent(state.htmlState, textAfter);
12235 else if (state.localMode.indent)
12236 return state.localMode.indent(state.localState, textAfter, line);
12237 else
12238 return CodeMirror.Pass;
12239 },
12240
12241 innerMode: function (state) {
12242 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12243 }
12244 };
12245 }, "xml", "javascript", "css");
12246
12247 CodeMirror.defineMIME("text/html", "htmlmixed");
12248 });
12249
12250 },{"../../lib/codemirror":14,"../css/css":15,"../javascript/javascript":17,"../xml/xml":18}],17:[function(require,module,exports){
12251 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12252 // Distributed under an MIT license: http://codemirror.net/LICENSE
12253
12254 (function(mod) {
12255 if (typeof exports == "object" && typeof module == "object") // CommonJS
12256 mod(require("../../lib/codemirror"));
12257 else if (typeof define == "function" && define.amd) // AMD
12258 define(["../../lib/codemirror"], mod);
12259 else // Plain browser env
12260 mod(CodeMirror);
12261 })(function(CodeMirror) {
12262 "use strict";
12263
12264 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12265 var indentUnit = config.indentUnit;
12266 var statementIndent = parserConfig.statementIndent;
12267 var jsonldMode = parserConfig.jsonld;
12268 var jsonMode = parserConfig.json || jsonldMode;
12269 var isTS = parserConfig.typescript;
12270 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12271
12272 // Tokenizer
12273
12274 var keywords = function(){
12275 function kw(type) {return {type: type, style: "keyword"};}
12276 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12277 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12278
12279 return {
12280 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12281 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12282 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12283 "function": kw("function"), "catch": kw("catch"),
12284 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12285 "in": operator, "typeof": operator, "instanceof": operator,
12286 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12287 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12288 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12289 "await": C
12290 };
12291 }();
12292
12293 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12294 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12295
12296 function readRegexp(stream) {
12297 var escaped = false, next, inSet = false;
12298 while ((next = stream.next()) != null) {
12299 if (!escaped) {
12300 if (next == "/" && !inSet) return;
12301 if (next == "[") inSet = true;
12302 else if (inSet && next == "]") inSet = false;
12303 }
12304 escaped = !escaped && next == "\\";
12305 }
12306 }
12307
12308 // Used as scratch variables to communicate multiple values without
12309 // consing up tons of objects.
12310 var type, content;
12311 function ret(tp, style, cont) {
12312 type = tp; content = cont;
12313 return style;
12314 }
12315 function tokenBase(stream, state) {
12316 var ch = stream.next();
12317 if (ch == '"' || ch == "'") {
12318 state.tokenize = tokenString(ch);
12319 return state.tokenize(stream, state);
12320 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
12321 return ret("number", "number");
12322 } else if (ch == "." && stream.match("..")) {
12323 return ret("spread", "meta");
12324 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12325 return ret(ch);
12326 } else if (ch == "=" && stream.eat(">")) {
12327 return ret("=>", "operator");
12328 } else if (ch == "0" && stream.eat(/x/i)) {
12329 stream.eatWhile(/[\da-f]/i);
12330 return ret("number", "number");
12331 } else if (ch == "0" && stream.eat(/o/i)) {
12332 stream.eatWhile(/[0-7]/i);
12333 return ret("number", "number");
12334 } else if (ch == "0" && stream.eat(/b/i)) {
12335 stream.eatWhile(/[01]/i);
12336 return ret("number", "number");
12337 } else if (/\d/.test(ch)) {
12338 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
12339 return ret("number", "number");
12340 } else if (ch == "/") {
12341 if (stream.eat("*")) {
12342 state.tokenize = tokenComment;
12343 return tokenComment(stream, state);
12344 } else if (stream.eat("/")) {
12345 stream.skipToEnd();
12346 return ret("comment", "comment");
12347 } else if (expressionAllowed(stream, state, 1)) {
12348 readRegexp(stream);
12349 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
12350 return ret("regexp", "string-2");
12351 } else {
12352 stream.eat("=");
12353 return ret("operator", "operator", stream.current());
12354 }
12355 } else if (ch == "`") {
12356 state.tokenize = tokenQuasi;
12357 return tokenQuasi(stream, state);
12358 } else if (ch == "#") {
12359 stream.skipToEnd();
12360 return ret("error", "error");
12361 } else if (isOperatorChar.test(ch)) {
12362 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12363 if (stream.eat("=")) {
12364 if (ch == "!" || ch == "=") stream.eat("=")
12365 } else if (/[<>*+\-]/.test(ch)) {
12366 stream.eat(ch)
12367 if (ch == ">") stream.eat(ch)
12368 }
12369 }
12370 return ret("operator", "operator", stream.current());
12371 } else if (wordRE.test(ch)) {
12372 stream.eatWhile(wordRE);
12373 var word = stream.current()
12374 if (state.lastType != ".") {
12375 if (keywords.propertyIsEnumerable(word)) {
12376 var kw = keywords[word]
12377 return ret(kw.type, kw.style, word)
12378 }
12379 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false))
12380 return ret("async", "keyword", word)
12381 }
12382 return ret("variable", "variable", word)
12383 }
12384 }
12385
12386 function tokenString(quote) {
12387 return function(stream, state) {
12388 var escaped = false, next;
12389 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12390 state.tokenize = tokenBase;
12391 return ret("jsonld-keyword", "meta");
12392 }
12393 while ((next = stream.next()) != null) {
12394 if (next == quote && !escaped) break;
12395 escaped = !escaped && next == "\\";
12396 }
12397 if (!escaped) state.tokenize = tokenBase;
12398 return ret("string", "string");
12399 };
12400 }
12401
12402 function tokenComment(stream, state) {
12403 var maybeEnd = false, ch;
12404 while (ch = stream.next()) {
12405 if (ch == "/" && maybeEnd) {
12406 state.tokenize = tokenBase;
12407 break;
12408 }
12409 maybeEnd = (ch == "*");
12410 }
12411 return ret("comment", "comment");
12412 }
12413
12414 function tokenQuasi(stream, state) {
12415 var escaped = false, next;
12416 while ((next = stream.next()) != null) {
12417 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12418 state.tokenize = tokenBase;
12419 break;
12420 }
12421 escaped = !escaped && next == "\\";
12422 }
12423 return ret("quasi", "string-2", stream.current());
12424 }
12425
12426 var brackets = "([{}])";
12427 // This is a crude lookahead trick to try and notice that we're
12428 // parsing the argument patterns for a fat-arrow function before we
12429 // actually hit the arrow token. It only works if the arrow is on
12430 // the same line as the arguments and there's no strange noise
12431 // (comments) in between. Fallback is to only notice when we hit the
12432 // arrow, and not declare the arguments as locals for the arrow
12433 // body.
12434 function findFatArrow(stream, state) {
12435 if (state.fatArrowAt) state.fatArrowAt = null;
12436 var arrow = stream.string.indexOf("=>", stream.start);
12437 if (arrow < 0) return;
12438
12439 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12440 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12441 if (m) arrow = m.index
12442 }
12443
12444 var depth = 0, sawSomething = false;
12445 for (var pos = arrow - 1; pos >= 0; --pos) {
12446 var ch = stream.string.charAt(pos);
12447 var bracket = brackets.indexOf(ch);
12448 if (bracket >= 0 && bracket < 3) {
12449 if (!depth) { ++pos; break; }
12450 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12451 } else if (bracket >= 3 && bracket < 6) {
12452 ++depth;
12453 } else if (wordRE.test(ch)) {
12454 sawSomething = true;
12455 } else if (/["'\/]/.test(ch)) {
12456 return;
12457 } else if (sawSomething && !depth) {
12458 ++pos;
12459 break;
12460 }
12461 }
12462 if (sawSomething && !depth) state.fatArrowAt = pos;
12463 }
12464
12465 // Parser
12466
12467 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12468
12469 function JSLexical(indented, column, type, align, prev, info) {
12470 this.indented = indented;
12471 this.column = column;
12472 this.type = type;
12473 this.prev = prev;
12474 this.info = info;
12475 if (align != null) this.align = align;
12476 }
12477
12478 function inScope(state, varname) {
12479 for (var v = state.localVars; v; v = v.next)
12480 if (v.name == varname) return true;
12481 for (var cx = state.context; cx; cx = cx.prev) {
12482 for (var v = cx.vars; v; v = v.next)
12483 if (v.name == varname) return true;
12484 }
12485 }
12486
12487 function parseJS(state, style, type, content, stream) {
12488 var cc = state.cc;
12489 // Communicate our context to the combinators.
12490 // (Less wasteful than consing up a hundred closures on every call.)
12491 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12492
12493 if (!state.lexical.hasOwnProperty("align"))
12494 state.lexical.align = true;
12495
12496 while(true) {
12497 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12498 if (combinator(type, content)) {
12499 while(cc.length && cc[cc.length - 1].lex)
12500 cc.pop()();
12501 if (cx.marked) return cx.marked;
12502 if (type == "variable" && inScope(state, content)) return "variable-2";
12503 return style;
12504 }
12505 }
12506 }
12507
12508 // Combinator utils
12509
12510 var cx = {state: null, column: null, marked: null, cc: null};
12511 function pass() {
12512 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12513 }
12514 function cont() {
12515 pass.apply(null, arguments);
12516 return true;
12517 }
12518 function register(varname) {
12519 function inList(list) {
12520 for (var v = list; v; v = v.next)
12521 if (v.name == varname) return true;
12522 return false;
12523 }
12524 var state = cx.state;
12525 cx.marked = "def";
12526 if (state.context) {
12527 if (inList(state.localVars)) return;
12528 state.localVars = {name: varname, next: state.localVars};
12529 } else {
12530 if (inList(state.globalVars)) return;
12531 if (parserConfig.globalVars)
12532 state.globalVars = {name: varname, next: state.globalVars};
12533 }
12534 }
12535
12536 function isModifier(name) {
12537 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12538 }
12539
12540 // Combinators
12541
12542 var defaultVars = {name: "this", next: {name: "arguments"}};
12543 function pushcontext() {
12544 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
12545 cx.state.localVars = defaultVars;
12546 }
12547 function popcontext() {
12548 cx.state.localVars = cx.state.context.vars;
12549 cx.state.context = cx.state.context.prev;
12550 }
12551 function pushlex(type, info) {
12552 var result = function() {
12553 var state = cx.state, indent = state.indented;
12554 if (state.lexical.type == "stat") indent = state.lexical.indented;
12555 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12556 indent = outer.indented;
12557 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12558 };
12559 result.lex = true;
12560 return result;
12561 }
12562 function poplex() {
12563 var state = cx.state;
12564 if (state.lexical.prev) {
12565 if (state.lexical.type == ")")
12566 state.indented = state.lexical.indented;
12567 state.lexical = state.lexical.prev;
12568 }
12569 }
12570 poplex.lex = true;
12571
12572 function expect(wanted) {
12573 function exp(type) {
12574 if (type == wanted) return cont();
12575 else if (wanted == ";") return pass();
12576 else return cont(exp);
12577 };
12578 return exp;
12579 }
12580
12581 function statement(type, value) {
12582 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
12583 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12584 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12585 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12586 if (type == "debugger") return cont(expect(";"));
12587 if (type == "{") return cont(pushlex("}"), block, poplex);
12588 if (type == ";") return cont();
12589 if (type == "if") {
12590 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12591 cx.state.cc.pop()();
12592 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12593 }
12594 if (type == "function") return cont(functiondef);
12595 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12596 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
12597 if (type == "variable") {
12598 if (isTS && value == "declare") {
12599 cx.marked = "keyword"
12600 return cont(statement)
12601 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12602 cx.marked = "keyword"
12603 if (value == "enum") return cont(enumdef);
12604 else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
12605 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12606 } else if (isTS && value == "namespace") {
12607 cx.marked = "keyword"
12608 return cont(pushlex("form"), expression, block, poplex)
12609 } else {
12610 return cont(pushlex("stat"), maybelabel);
12611 }
12612 }
12613 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
12614 block, poplex, poplex);
12615 if (type == "case") return cont(expression, expect(":"));
12616 if (type == "default") return cont(expect(":"));
12617 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
12618 statement, poplex, popcontext);
12619 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12620 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12621 if (type == "async") return cont(statement)
12622 if (value == "@") return cont(expression, statement)
12623 return pass(pushlex("stat"), expression, expect(";"), poplex);
12624 }
12625 function expression(type, value) {
12626 return expressionInner(type, value, false);
12627 }
12628 function expressionNoComma(type, value) {
12629 return expressionInner(type, value, true);
12630 }
12631 function parenExpr(type) {
12632 if (type != "(") return pass()
12633 return cont(pushlex(")"), expression, expect(")"), poplex)
12634 }
12635 function expressionInner(type, value, noComma) {
12636 if (cx.state.fatArrowAt == cx.stream.start) {
12637 var body = noComma ? arrowBodyNoComma : arrowBody;
12638 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12639 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12640 }
12641
12642 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12643 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12644 if (type == "function") return cont(functiondef, maybeop);
12645 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12646 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12647 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12648 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12649 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12650 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12651 if (type == "quasi") return pass(quasi, maybeop);
12652 if (type == "new") return cont(maybeTarget(noComma));
12653 return cont();
12654 }
12655 function maybeexpression(type) {
12656 if (type.match(/[;\}\)\],]/)) return pass();
12657 return pass(expression);
12658 }
12659
12660 function maybeoperatorComma(type, value) {
12661 if (type == ",") return cont(expression);
12662 return maybeoperatorNoComma(type, value, false);
12663 }
12664 function maybeoperatorNoComma(type, value, noComma) {
12665 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12666 var expr = noComma == false ? expression : expressionNoComma;
12667 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12668 if (type == "operator") {
12669 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12670 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12671 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12672 if (value == "?") return cont(expression, expect(":"), expr);
12673 return cont(expr);
12674 }
12675 if (type == "quasi") { return pass(quasi, me); }
12676 if (type == ";") return;
12677 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12678 if (type == ".") return cont(property, me);
12679 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12680 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12681 if (type == "regexp") {
12682 cx.state.lastType = cx.marked = "operator"
12683 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12684 return cont(expr)
12685 }
12686 }
12687 function quasi(type, value) {
12688 if (type != "quasi") return pass();
12689 if (value.slice(value.length - 2) != "${") return cont(quasi);
12690 return cont(expression, continueQuasi);
12691 }
12692 function continueQuasi(type) {
12693 if (type == "}") {
12694 cx.marked = "string-2";
12695 cx.state.tokenize = tokenQuasi;
12696 return cont(quasi);
12697 }
12698 }
12699 function arrowBody(type) {
12700 findFatArrow(cx.stream, cx.state);
12701 return pass(type == "{" ? statement : expression);
12702 }
12703 function arrowBodyNoComma(type) {
12704 findFatArrow(cx.stream, cx.state);
12705 return pass(type == "{" ? statement : expressionNoComma);
12706 }
12707 function maybeTarget(noComma) {
12708 return function(type) {
12709 if (type == ".") return cont(noComma ? targetNoComma : target);
12710 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12711 else return pass(noComma ? expressionNoComma : expression);
12712 };
12713 }
12714 function target(_, value) {
12715 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12716 }
12717 function targetNoComma(_, value) {
12718 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12719 }
12720 function maybelabel(type) {
12721 if (type == ":") return cont(poplex, statement);
12722 return pass(maybeoperatorComma, expect(";"), poplex);
12723 }
12724 function property(type) {
12725 if (type == "variable") {cx.marked = "property"; return cont();}
12726 }
12727 function objprop(type, value) {
12728 if (type == "async") {
12729 cx.marked = "property";
12730 return cont(objprop);
12731 } else if (type == "variable" || cx.style == "keyword") {
12732 cx.marked = "property";
12733 if (value == "get" || value == "set") return cont(getterSetter);
12734 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12735 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12736 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12737 return cont(afterprop);
12738 } else if (type == "number" || type == "string") {
12739 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12740 return cont(afterprop);
12741 } else if (type == "jsonld-keyword") {
12742 return cont(afterprop);
12743 } else if (isTS && isModifier(value)) {
12744 cx.marked = "keyword"
12745 return cont(objprop)
12746 } else if (type == "[") {
12747 return cont(expression, maybetype, expect("]"), afterprop);
12748 } else if (type == "spread") {
12749 return cont(expressionNoComma, afterprop);
12750 } else if (value == "*") {
12751 cx.marked = "keyword";
12752 return cont(objprop);
12753 } else if (type == ":") {
12754 return pass(afterprop)
12755 }
12756 }
12757 function getterSetter(type) {
12758 if (type != "variable") return pass(afterprop);
12759 cx.marked = "property";
12760 return cont(functiondef);
12761 }
12762 function afterprop(type) {
12763 if (type == ":") return cont(expressionNoComma);
12764 if (type == "(") return pass(functiondef);
12765 }
12766 function commasep(what, end, sep) {
12767 function proceed(type, value) {
12768 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12769 var lex = cx.state.lexical;
12770 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12771 return cont(function(type, value) {
12772 if (type == end || value == end) return pass()
12773 return pass(what)
12774 }, proceed);
12775 }
12776 if (type == end || value == end) return cont();
12777 return cont(expect(end));
12778 }
12779 return function(type, value) {
12780 if (type == end || value == end) return cont();
12781 return pass(what, proceed);
12782 };
12783 }
12784 function contCommasep(what, end, info) {
12785 for (var i = 3; i < arguments.length; i++)
12786 cx.cc.push(arguments[i]);
12787 return cont(pushlex(end, info), commasep(what, end), poplex);
12788 }
12789 function block(type) {
12790 if (type == "}") return cont();
12791 return pass(statement, block);
12792 }
12793 function maybetype(type, value) {
12794 if (isTS) {
12795 if (type == ":") return cont(typeexpr);
12796 if (value == "?") return cont(maybetype);
12797 }
12798 }
12799 function mayberettype(type) {
12800 if (isTS && type == ":") {
12801 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12802 else return cont(typeexpr)
12803 }
12804 }
12805 function isKW(_, value) {
12806 if (value == "is") {
12807 cx.marked = "keyword"
12808 return cont()
12809 }
12810 }
12811 function typeexpr(type, value) {
12812 if (type == "variable" || value == "void") {
12813 if (value == "keyof") {
12814 cx.marked = "keyword"
12815 return cont(typeexpr)
12816 } else {
12817 cx.marked = "type"
12818 return cont(afterType)
12819 }
12820 }
12821 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12822 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12823 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12824 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
12825 }
12826 function maybeReturnType(type) {
12827 if (type == "=>") return cont(typeexpr)
12828 }
12829 function typeprop(type, value) {
12830 if (type == "variable" || cx.style == "keyword") {
12831 cx.marked = "property"
12832 return cont(typeprop)
12833 } else if (value == "?") {
12834 return cont(typeprop)
12835 } else if (type == ":") {
12836 return cont(typeexpr)
12837 } else if (type == "[") {
12838 return cont(expression, maybetype, expect("]"), typeprop)
12839 }
12840 }
12841 function typearg(type) {
12842 if (type == "variable") return cont(typearg)
12843 else if (type == ":") return cont(typeexpr)
12844 }
12845 function afterType(type, value) {
12846 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12847 if (value == "|" || type == ".") return cont(typeexpr)
12848 if (type == "[") return cont(expect("]"), afterType)
12849 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
12850 }
12851 function maybeTypeArgs(_, value) {
12852 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12853 }
12854 function typeparam() {
12855 return pass(typeexpr, maybeTypeDefault)
12856 }
12857 function maybeTypeDefault(_, value) {
12858 if (value == "=") return cont(typeexpr)
12859 }
12860 function vardef(_, value) {
12861 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
12862 return pass(pattern, maybetype, maybeAssign, vardefCont);
12863 }
12864 function pattern(type, value) {
12865 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
12866 if (type == "variable") { register(value); return cont(); }
12867 if (type == "spread") return cont(pattern);
12868 if (type == "[") return contCommasep(pattern, "]");
12869 if (type == "{") return contCommasep(proppattern, "}");
12870 }
12871 function proppattern(type, value) {
12872 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12873 register(value);
12874 return cont(maybeAssign);
12875 }
12876 if (type == "variable") cx.marked = "property";
12877 if (type == "spread") return cont(pattern);
12878 if (type == "}") return pass();
12879 return cont(expect(":"), pattern, maybeAssign);
12880 }
12881 function maybeAssign(_type, value) {
12882 if (value == "=") return cont(expressionNoComma);
12883 }
12884 function vardefCont(type) {
12885 if (type == ",") return cont(vardef);
12886 }
12887 function maybeelse(type, value) {
12888 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
12889 }
12890 function forspec(type) {
12891 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
12892 }
12893 function forspec1(type) {
12894 if (type == "var") return cont(vardef, expect(";"), forspec2);
12895 if (type == ";") return cont(forspec2);
12896 if (type == "variable") return cont(formaybeinof);
12897 return pass(expression, expect(";"), forspec2);
12898 }
12899 function formaybeinof(_type, value) {
12900 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12901 return cont(maybeoperatorComma, forspec2);
12902 }
12903 function forspec2(type, value) {
12904 if (type == ";") return cont(forspec3);
12905 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
12906 return pass(expression, expect(";"), forspec3);
12907 }
12908 function forspec3(type) {
12909 if (type != ")") cont(expression);
12910 }
12911 function functiondef(type, value) {
12912 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
12913 if (type == "variable") {register(value); return cont(functiondef);}
12914 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
12915 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
12916 }
12917 function funarg(type, value) {
12918 if (value == "@") cont(expression, funarg)
12919 if (type == "spread") return cont(funarg);
12920 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
12921 return pass(pattern, maybetype, maybeAssign);
12922 }
12923 function classExpression(type, value) {
12924 // Class expressions may have an optional name.
12925 if (type == "variable") return className(type, value);
12926 return classNameAfter(type, value);
12927 }
12928 function className(type, value) {
12929 if (type == "variable") {register(value); return cont(classNameAfter);}
12930 }
12931 function classNameAfter(type, value) {
12932 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
12933 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
12934 if (value == "implements") cx.marked = "keyword";
12935 return cont(isTS ? typeexpr : expression, classNameAfter);
12936 }
12937 if (type == "{") return cont(pushlex("}"), classBody, poplex);
12938 }
12939 function classBody(type, value) {
12940 if (type == "async" ||
12941 (type == "variable" &&
12942 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
12943 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
12944 cx.marked = "keyword";
12945 return cont(classBody);
12946 }
12947 if (type == "variable" || cx.style == "keyword") {
12948 cx.marked = "property";
12949 return cont(isTS ? classfield : functiondef, classBody);
12950 }
12951 if (type == "[")
12952 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
12953 if (value == "*") {
12954 cx.marked = "keyword";
12955 return cont(classBody);
12956 }
12957 if (type == ";") return cont(classBody);
12958 if (type == "}") return cont();
12959 if (value == "@") return cont(expression, classBody)
12960 }
12961 function classfield(type, value) {
12962 if (value == "?") return cont(classfield)
12963 if (type == ":") return cont(typeexpr, maybeAssign)
12964 if (value == "=") return cont(expressionNoComma)
12965 return pass(functiondef)
12966 }
12967 function afterExport(type, value) {
12968 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
12969 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
12970 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
12971 return pass(statement);
12972 }
12973 function exportField(type, value) {
12974 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
12975 if (type == "variable") return pass(expressionNoComma, exportField);
12976 }
12977 function afterImport(type) {
12978 if (type == "string") return cont();
12979 return pass(importSpec, maybeMoreImports, maybeFrom);
12980 }
12981 function importSpec(type, value) {
12982 if (type == "{") return contCommasep(importSpec, "}");
12983 if (type == "variable") register(value);
12984 if (value == "*") cx.marked = "keyword";
12985 return cont(maybeAs);
12986 }
12987 function maybeMoreImports(type) {
12988 if (type == ",") return cont(importSpec, maybeMoreImports)
12989 }
12990 function maybeAs(_type, value) {
12991 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
12992 }
12993 function maybeFrom(_type, value) {
12994 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
12995 }
12996 function arrayLiteral(type) {
12997 if (type == "]") return cont();
12998 return pass(commasep(expressionNoComma, "]"));
12999 }
13000 function enumdef() {
13001 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
13002 }
13003 function enummember() {
13004 return pass(pattern, maybeAssign);
13005 }
13006
13007 function isContinuedStatement(state, textAfter) {
13008 return state.lastType == "operator" || state.lastType == "," ||
13009 isOperatorChar.test(textAfter.charAt(0)) ||
13010 /[,.]/.test(textAfter.charAt(0));
13011 }
13012
13013 function expressionAllowed(stream, state, backUp) {
13014 return state.tokenize == tokenBase &&
13015 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
13016 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
13017 }
13018
13019 // Interface
13020
13021 return {
13022 startState: function(basecolumn) {
13023 var state = {
13024 tokenize: tokenBase,
13025 lastType: "sof",
13026 cc: [],
13027 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
13028 localVars: parserConfig.localVars,
13029 context: parserConfig.localVars && {vars: parserConfig.localVars},
13030 indented: basecolumn || 0
13031 };
13032 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
13033 state.globalVars = parserConfig.globalVars;
13034 return state;
13035 },
13036
13037 token: function(stream, state) {
13038 if (stream.sol()) {
13039 if (!state.lexical.hasOwnProperty("align"))
13040 state.lexical.align = false;
13041 state.indented = stream.indentation();
13042 findFatArrow(stream, state);
13043 }
13044 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
13045 var style = state.tokenize(stream, state);
13046 if (type == "comment") return style;
13047 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
13048 return parseJS(state, style, type, content, stream);
13049 },
13050
13051 indent: function(state, textAfter) {
13052 if (state.tokenize == tokenComment) return CodeMirror.Pass;
13053 if (state.tokenize != tokenBase) return 0;
13054 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
13055 // Kludge to prevent 'maybelse' from blocking lexical scope pops
13056 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
13057 var c = state.cc[i];
13058 if (c == poplex) lexical = lexical.prev;
13059 else if (c != maybeelse) break;
13060 }
13061 while ((lexical.type == "stat" || lexical.type == "form") &&
13062 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
13063 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
13064 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
13065 lexical = lexical.prev;
13066 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
13067 lexical = lexical.prev;
13068 var type = lexical.type, closing = firstChar == type;
13069
13070 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
13071 else if (type == "form" && firstChar == "{") return lexical.indented;
13072 else if (type == "form") return lexical.indented + indentUnit;
13073 else if (type == "stat")
13074 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
13075 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
13076 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
13077 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
13078 else return lexical.indented + (closing ? 0 : indentUnit);
13079 },
13080
13081 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
13082 blockCommentStart: jsonMode ? null : "/*",
13083 blockCommentEnd: jsonMode ? null : "*/",
13084 blockCommentContinue: jsonMode ? null : " * ",
13085 lineComment: jsonMode ? null : "//",
13086 fold: "brace",
13087 closeBrackets: "()[]{}''\"\"``",
13088
13089 helperType: jsonMode ? "json" : "javascript",
13090 jsonldMode: jsonldMode,
13091 jsonMode: jsonMode,
13092
13093 expressionAllowed: expressionAllowed,
13094
13095 skipExpression: function(state) {
13096 var top = state.cc[state.cc.length - 1]
13097 if (top == expression || top == expressionNoComma) state.cc.pop()
13098 }
13099 };
13100 });
13101
13102 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
13103
13104 CodeMirror.defineMIME("text/javascript", "javascript");
13105 CodeMirror.defineMIME("text/ecmascript", "javascript");
13106 CodeMirror.defineMIME("application/javascript", "javascript");
13107 CodeMirror.defineMIME("application/x-javascript", "javascript");
13108 CodeMirror.defineMIME("application/ecmascript", "javascript");
13109 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
13110 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
13111 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
13112 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
13113 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
13114
13115 });
13116
13117 },{"../../lib/codemirror":14}],18:[function(require,module,exports){
13118 // CodeMirror, copyright (c) by Marijn Haverbeke and others
13119 // Distributed under an MIT license: http://codemirror.net/LICENSE
13120
13121 (function(mod) {
13122 if (typeof exports == "object" && typeof module == "object") // CommonJS
13123 mod(require("../../lib/codemirror"));
13124 else if (typeof define == "function" && define.amd) // AMD
13125 define(["../../lib/codemirror"], mod);
13126 else // Plain browser env
13127 mod(CodeMirror);
13128 })(function(CodeMirror) {
13129 "use strict";
13130
13131 var htmlConfig = {
13132 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13133 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13134 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13135 'track': true, 'wbr': true, 'menuitem': true},
13136 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13137 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13138 'th': true, 'tr': true},
13139 contextGrabbers: {
13140 'dd': {'dd': true, 'dt': true},
13141 'dt': {'dd': true, 'dt': true},
13142 'li': {'li': true},
13143 'option': {'option': true, 'optgroup': true},
13144 'optgroup': {'optgroup': true},
13145 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13146 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13147 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13148 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13149 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13150 'rp': {'rp': true, 'rt': true},
13151 'rt': {'rp': true, 'rt': true},
13152 'tbody': {'tbody': true, 'tfoot': true},
13153 'td': {'td': true, 'th': true},
13154 'tfoot': {'tbody': true},
13155 'th': {'td': true, 'th': true},
13156 'thead': {'tbody': true, 'tfoot': true},
13157 'tr': {'tr': true}
13158 },
13159 doNotIndent: {"pre": true},
13160 allowUnquoted: true,
13161 allowMissing: true,
13162 caseFold: true
13163 }
13164
13165 var xmlConfig = {
13166 autoSelfClosers: {},
13167 implicitlyClosed: {},
13168 contextGrabbers: {},
13169 doNotIndent: {},
13170 allowUnquoted: false,
13171 allowMissing: false,
13172 allowMissingTagName: false,
13173 caseFold: false
13174 }
13175
13176 CodeMirror.defineMode("xml", function(editorConf, config_) {
13177 var indentUnit = editorConf.indentUnit
13178 var config = {}
13179 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13180 for (var prop in defaults) config[prop] = defaults[prop]
13181 for (var prop in config_) config[prop] = config_[prop]
13182
13183 // Return variables for tokenizers
13184 var type, setStyle;
13185
13186 function inText(stream, state) {
13187 function chain(parser) {
13188 state.tokenize = parser;
13189 return parser(stream, state);
13190 }
13191
13192 var ch = stream.next();
13193 if (ch == "<") {
13194 if (stream.eat("!")) {
13195 if (stream.eat("[")) {
13196 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13197 else return null;
13198 } else if (stream.match("--")) {
13199 return chain(inBlock("comment", "-->"));
13200 } else if (stream.match("DOCTYPE", true, true)) {
13201 stream.eatWhile(/[\w\._\-]/);
13202 return chain(doctype(1));
13203 } else {
13204 return null;
13205 }
13206 } else if (stream.eat("?")) {
13207 stream.eatWhile(/[\w\._\-]/);
13208 state.tokenize = inBlock("meta", "?>");
13209 return "meta";
13210 } else {
13211 type = stream.eat("/") ? "closeTag" : "openTag";
13212 state.tokenize = inTag;
13213 return "tag bracket";
13214 }
13215 } else if (ch == "&") {
13216 var ok;
13217 if (stream.eat("#")) {
13218 if (stream.eat("x")) {
13219 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13220 } else {
13221 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13222 }
13223 } else {
13224 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13225 }
13226 return ok ? "atom" : "error";
13227 } else {
13228 stream.eatWhile(/[^&<]/);
13229 return null;
13230 }
13231 }
13232 inText.isInText = true;
13233
13234 function inTag(stream, state) {
13235 var ch = stream.next();
13236 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13237 state.tokenize = inText;
13238 type = ch == ">" ? "endTag" : "selfcloseTag";
13239 return "tag bracket";
13240 } else if (ch == "=") {
13241 type = "equals";
13242 return null;
13243 } else if (ch == "<") {
13244 state.tokenize = inText;
13245 state.state = baseState;
13246 state.tagName = state.tagStart = null;
13247 var next = state.tokenize(stream, state);
13248 return next ? next + " tag error" : "tag error";
13249 } else if (/[\'\"]/.test(ch)) {
13250 state.tokenize = inAttribute(ch);
13251 state.stringStartCol = stream.column();
13252 return state.tokenize(stream, state);
13253 } else {
13254 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13255 return "word";
13256 }
13257 }
13258
13259 function inAttribute(quote) {
13260 var closure = function(stream, state) {
13261 while (!stream.eol()) {
13262 if (stream.next() == quote) {
13263 state.tokenize = inTag;
13264 break;
13265 }
13266 }
13267 return "string";
13268 };
13269 closure.isInAttribute = true;
13270 return closure;
13271 }
13272
13273 function inBlock(style, terminator) {
13274 return function(stream, state) {
13275 while (!stream.eol()) {
13276 if (stream.match(terminator)) {
13277 state.tokenize = inText;
13278 break;
13279 }
13280 stream.next();
13281 }
13282 return style;
13283 };
13284 }
13285 function doctype(depth) {
13286 return function(stream, state) {
13287 var ch;
13288 while ((ch = stream.next()) != null) {
13289 if (ch == "<") {
13290 state.tokenize = doctype(depth + 1);
13291 return state.tokenize(stream, state);
13292 } else if (ch == ">") {
13293 if (depth == 1) {
13294 state.tokenize = inText;
13295 break;
13296 } else {
13297 state.tokenize = doctype(depth - 1);
13298 return state.tokenize(stream, state);
13299 }
13300 }
13301 }
13302 return "meta";
13303 };
13304 }
13305
13306 function Context(state, tagName, startOfLine) {
13307 this.prev = state.context;
13308 this.tagName = tagName;
13309 this.indent = state.indented;
13310 this.startOfLine = startOfLine;
13311 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13312 this.noIndent = true;
13313 }
13314 function popContext(state) {
13315 if (state.context) state.context = state.context.prev;
13316 }
13317 function maybePopContext(state, nextTagName) {
13318 var parentTagName;
13319 while (true) {
13320 if (!state.context) {
13321 return;
13322 }
13323 parentTagName = state.context.tagName;
13324 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13325 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13326 return;
13327 }
13328 popContext(state);
13329 }
13330 }
13331
13332 function baseState(type, stream, state) {
13333 if (type == "openTag") {
13334 state.tagStart = stream.column();
13335 return tagNameState;
13336 } else if (type == "closeTag") {
13337 return closeTagNameState;
13338 } else {
13339 return baseState;
13340 }
13341 }
13342 function tagNameState(type, stream, state) {
13343 if (type == "word") {
13344 state.tagName = stream.current();
13345 setStyle = "tag";
13346 return attrState;
13347 } else if (config.allowMissingTagName && type == "endTag") {
13348 setStyle = "tag bracket";
13349 return attrState(type, stream, state);
13350 } else {
13351 setStyle = "error";
13352 return tagNameState;
13353 }
13354 }
13355 function closeTagNameState(type, stream, state) {
13356 if (type == "word") {
13357 var tagName = stream.current();
13358 if (state.context && state.context.tagName != tagName &&
13359 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13360 popContext(state);
13361 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13362 setStyle = "tag";
13363 return closeState;
13364 } else {
13365 setStyle = "tag error";
13366 return closeStateErr;
13367 }
13368 } else if (config.allowMissingTagName && type == "endTag") {
13369 setStyle = "tag bracket";
13370 return closeState(type, stream, state);
13371 } else {
13372 setStyle = "error";
13373 return closeStateErr;
13374 }
13375 }
13376
13377 function closeState(type, _stream, state) {
13378 if (type != "endTag") {
13379 setStyle = "error";
13380 return closeState;
13381 }
13382 popContext(state);
13383 return baseState;
13384 }
13385 function closeStateErr(type, stream, state) {
13386 setStyle = "error";
13387 return closeState(type, stream, state);
13388 }
13389
13390 function attrState(type, _stream, state) {
13391 if (type == "word") {
13392 setStyle = "attribute";
13393 return attrEqState;
13394 } else if (type == "endTag" || type == "selfcloseTag") {
13395 var tagName = state.tagName, tagStart = state.tagStart;
13396 state.tagName = state.tagStart = null;
13397 if (type == "selfcloseTag" ||
13398 config.autoSelfClosers.hasOwnProperty(tagName)) {
13399 maybePopContext(state, tagName);
13400 } else {
13401 maybePopContext(state, tagName);
13402 state.context = new Context(state, tagName, tagStart == state.indented);
13403 }
13404 return baseState;
13405 }
13406 setStyle = "error";
13407 return attrState;
13408 }
13409 function attrEqState(type, stream, state) {
13410 if (type == "equals") return attrValueState;
13411 if (!config.allowMissing) setStyle = "error";
13412 return attrState(type, stream, state);
13413 }
13414 function attrValueState(type, stream, state) {
13415 if (type == "string") return attrContinuedState;
13416 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13417 setStyle = "error";
13418 return attrState(type, stream, state);
13419 }
13420 function attrContinuedState(type, stream, state) {
13421 if (type == "string") return attrContinuedState;
13422 return attrState(type, stream, state);
13423 }
13424
13425 return {
13426 startState: function(baseIndent) {
13427 var state = {tokenize: inText,
13428 state: baseState,
13429 indented: baseIndent || 0,
13430 tagName: null, tagStart: null,
13431 context: null}
13432 if (baseIndent != null) state.baseIndent = baseIndent
13433 return state
13434 },
13435
13436 token: function(stream, state) {
13437 if (!state.tagName && stream.sol())
13438 state.indented = stream.indentation();
13439
13440 if (stream.eatSpace()) return null;
13441 type = null;
13442 var style = state.tokenize(stream, state);
13443 if ((style || type) && style != "comment") {
13444 setStyle = null;
13445 state.state = state.state(type || style, stream, state);
13446 if (setStyle)
13447 style = setStyle == "error" ? style + " error" : setStyle;
13448 }
13449 return style;
13450 },
13451
13452 indent: function(state, textAfter, fullLine) {
13453 var context = state.context;
13454 // Indent multi-line strings (e.g. css).
13455 if (state.tokenize.isInAttribute) {
13456 if (state.tagStart == state.indented)
13457 return state.stringStartCol + 1;
13458 else
13459 return state.indented + indentUnit;
13460 }
13461 if (context && context.noIndent) return CodeMirror.Pass;
13462 if (state.tokenize != inTag && state.tokenize != inText)
13463 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13464 // Indent the starts of attribute names.
13465 if (state.tagName) {
13466 if (config.multilineTagIndentPastTag !== false)
13467 return state.tagStart + state.tagName.length + 2;
13468 else
13469 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13470 }
13471 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13472 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13473 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13474 while (context) {
13475 if (context.tagName == tagAfter[2]) {
13476 context = context.prev;
13477 break;
13478 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13479 context = context.prev;
13480 } else {
13481 break;
13482 }
13483 }
13484 } else if (tagAfter) { // Opening tag spotted
13485 while (context) {
13486 var grabbers = config.contextGrabbers[context.tagName];
13487 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13488 context = context.prev;
13489 else
13490 break;
13491 }
13492 }
13493 while (context && context.prev && !context.startOfLine)
13494 context = context.prev;
13495 if (context) return context.indent + indentUnit;
13496 else return state.baseIndent || 0;
13497 },
13498
13499 electricInput: /<\/[\s\w:]+>$/,
13500 blockCommentStart: "<!--",
13501 blockCommentEnd: "-->",
13502
13503 configuration: config.htmlMode ? "html" : "xml",
13504 helperType: config.htmlMode ? "html" : "xml",
13505
13506 skipAttribute: function(state) {
13507 if (state.state == attrValueState)
13508 state.state = attrState
13509 }
13510 };
13511 });
13512
13513 CodeMirror.defineMIME("text/xml", "xml");
13514 CodeMirror.defineMIME("application/xml", "xml");
13515 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13516 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13517
13518 });
13519
13520 },{"../../lib/codemirror":14}],19:[function(require,module,exports){
13521 (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);};};}});
13522
13523
13524 },{}],20:[function(require,module,exports){
13525 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;
13526
13527
13528 },{}],21:[function(require,module,exports){
13529 (function (global, factory) {
13530 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13531 typeof define === 'function' && define.amd ? define(factory) :
13532 (global.preactRenderToString = factory());
13533 }(this, (function () {
13534
13535 var NON_DIMENSION_PROPS = {
13536 boxFlex: 1, boxFlexGroup: 1, columnCount: 1, fillOpacity: 1, flex: 1, flexGrow: 1,
13537 flexPositive: 1, flexShrink: 1, flexNegative: 1, fontWeight: 1, lineClamp: 1, lineHeight: 1,
13538 opacity: 1, order: 1, orphans: 1, strokeOpacity: 1, widows: 1, zIndex: 1, zoom: 1
13539 };
13540
13541 var ESC = {
13542 '<': '&lt;',
13543 '>': '&gt;',
13544 '"': '&quot;',
13545 '&': '&amp;'
13546 };
13547
13548 var objectKeys = Object.keys || function (obj) {
13549 var keys = [];
13550 for (var i in obj) {
13551 if (obj.hasOwnProperty(i)) keys.push(i);
13552 }return keys;
13553 };
13554
13555 var encodeEntities = function encodeEntities(s) {
13556 return String(s).replace(/[<>"&]/g, escapeChar);
13557 };
13558
13559 var escapeChar = function escapeChar(a) {
13560 return ESC[a] || a;
13561 };
13562
13563 var falsey = function falsey(v) {
13564 return v == null || v === false;
13565 };
13566
13567 var memoize = function memoize(fn) {
13568 var mem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13569 return function (v) {
13570 return mem[v] || (mem[v] = fn(v));
13571 };
13572 };
13573
13574 var indent = function indent(s, char) {
13575 return String(s).replace(/(\n+)/g, '$1' + (char || '\t'));
13576 };
13577
13578 var isLargeString = function isLargeString(s, length, ignoreLines) {
13579 return String(s).length > (length || 40) || !ignoreLines && String(s).indexOf('\n') !== -1 || String(s).indexOf('<') !== -1;
13580 };
13581
13582 function styleObjToCss(s) {
13583 var str = '';
13584 for (var prop in s) {
13585 var val = s[prop];
13586 if (val != null) {
13587 if (str) str += ' ';
13588 str += jsToCss(prop);
13589 str += ': ';
13590 str += val;
13591 if (typeof val === 'number' && !NON_DIMENSION_PROPS[prop]) {
13592 str += 'px';
13593 }
13594 str += ';';
13595 }
13596 }
13597 return str || undefined;
13598 }
13599
13600 function hashToClassName(c) {
13601 var str = '';
13602 for (var prop in c) {
13603 if (c[prop]) {
13604 if (str) str += ' ';
13605 str += prop;
13606 }
13607 }
13608 return str;
13609 }
13610
13611 var jsToCss = memoize(function (s) {
13612 return s.replace(/([A-Z])/g, '-$1').toLowerCase();
13613 });
13614
13615 function assign(obj, props) {
13616 for (var i in props) {
13617 obj[i] = props[i];
13618 }return obj;
13619 }
13620
13621 function getNodeProps(vnode) {
13622 var defaultProps = vnode.nodeName.defaultProps,
13623 props = assign({}, defaultProps || vnode.attributes);
13624 if (defaultProps) assign(props, vnode.attributes);
13625 if (vnode.children) props.children = vnode.children;
13626 return props;
13627 }
13628
13629 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; };
13630
13631 var SHALLOW = { shallow: true };
13632
13633 var UNNAMED = [];
13634
13635 var EMPTY = {};
13636
13637 var VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
13638
13639 renderToString.render = renderToString;
13640
13641 var shallowRender = function shallowRender(vnode, context) {
13642 return renderToString(vnode, context, SHALLOW);
13643 };
13644
13645 function renderToString(vnode, context, opts, inner, isSvgMode) {
13646 var _ref = vnode || EMPTY,
13647 nodeName = _ref.nodeName,
13648 attributes = _ref.attributes,
13649 children = _ref.children,
13650 isComponent = false;
13651
13652 context = context || {};
13653 opts = opts || {};
13654
13655 var pretty = opts.pretty,
13656 indentChar = typeof pretty === 'string' ? pretty : '\t';
13657
13658 if (vnode == null || typeof vnode === 'boolean') {
13659 return '';
13660 }
13661
13662 if ((typeof vnode === 'undefined' ? 'undefined' : _typeof(vnode)) !== 'object' && !nodeName) {
13663 return encodeEntities(vnode);
13664 }
13665
13666 if (typeof nodeName === 'function') {
13667 isComponent = true;
13668 if (opts.shallow && (inner || opts.renderRootComponent === false)) {
13669 nodeName = getComponentName(nodeName);
13670 } else {
13671 var props = getNodeProps(vnode),
13672 rendered = void 0;
13673
13674 if (!nodeName.prototype || typeof nodeName.prototype.render !== 'function') {
13675 rendered = nodeName(props, context);
13676 } else {
13677 var c = new nodeName(props, context);
13678
13679 c._disable = c.__x = true;
13680 c.props = props;
13681 c.context = context;
13682 if (c.componentWillMount) c.componentWillMount();
13683 rendered = c.render(c.props, c.state, c.context);
13684
13685 if (c.getChildContext) {
13686 context = assign(assign({}, context), c.getChildContext());
13687 }
13688 }
13689
13690 return renderToString(rendered, context, opts, opts.shallowHighOrder !== false);
13691 }
13692 }
13693
13694 var s = '',
13695 html = void 0;
13696
13697 if (attributes) {
13698 var attrs = objectKeys(attributes);
13699
13700 if (opts && opts.sortAttributes === true) attrs.sort();
13701
13702 for (var i = 0; i < attrs.length; i++) {
13703 var name = attrs[i],
13704 v = attributes[name];
13705 if (name === 'children') continue;
13706 if (!(opts && opts.allAttributes) && (name === 'key' || name === 'ref')) continue;
13707
13708 if (name === 'className') {
13709 if (attributes['class']) continue;
13710 name = 'class';
13711 } else if (isSvgMode && name.match(/^xlink\:?(.+)/)) {
13712 name = name.toLowerCase().replace(/^xlink\:?(.+)/, 'xlink:$1');
13713 }
13714
13715 if (name === 'class' && v && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object') {
13716 v = hashToClassName(v);
13717 } else if (name === 'style' && v && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object') {
13718 v = styleObjToCss(v);
13719 }
13720
13721 var hooked = opts.attributeHook && opts.attributeHook(name, v, context, opts, isComponent);
13722 if (hooked || hooked === '') {
13723 s += hooked;
13724 continue;
13725 }
13726
13727 if (name === 'dangerouslySetInnerHTML') {
13728 html = v && v.__html;
13729 } else if ((v || v === 0 || v === '') && typeof v !== 'function') {
13730 if (v === true || v === '') {
13731 v = name;
13732
13733 if (!opts || !opts.xml) {
13734 s += ' ' + name;
13735 continue;
13736 }
13737 }
13738 s += ' ' + name + '="' + encodeEntities(v) + '"';
13739 }
13740 }
13741 }
13742
13743 var sub = s.replace(/^\n\s*/, ' ');
13744 if (sub !== s && !~sub.indexOf('\n')) s = sub;else if (pretty && ~s.indexOf('\n')) s += '\n';
13745
13746 s = '<' + nodeName + s + '>';
13747
13748 if (VOID_ELEMENTS.indexOf(nodeName) > -1) {
13749 s = s.replace(/>$/, ' />');
13750 }
13751
13752 if (html) {
13753 if (pretty && isLargeString(html)) {
13754 html = '\n' + indentChar + indent(html, indentChar);
13755 }
13756 s += html;
13757 } else {
13758 var len = children && children.length,
13759 pieces = [],
13760 hasLarge = ~s.indexOf('\n');
13761 for (var _i = 0; _i < len; _i++) {
13762 var child = children[_i];
13763 if (!falsey(child)) {
13764 var childSvgMode = nodeName === 'svg' ? true : nodeName === 'foreignObject' ? false : isSvgMode,
13765 ret = renderToString(child, context, opts, true, childSvgMode);
13766 if (!hasLarge && pretty && isLargeString(ret)) hasLarge = true;
13767 if (ret) pieces.push(ret);
13768 }
13769 }
13770 if (pretty && hasLarge) {
13771 for (var _i2 = pieces.length; _i2--;) {
13772 pieces[_i2] = '\n' + indentChar + indent(pieces[_i2], indentChar);
13773 }
13774 }
13775 if (pieces.length) {
13776 s += pieces.join('');
13777 } else if (opts && opts.xml) {
13778 return s.substring(0, s.length - 1) + ' />';
13779 }
13780 }
13781
13782 if (VOID_ELEMENTS.indexOf(nodeName) === -1) {
13783 if (pretty && ~s.indexOf('\n')) s += '\n';
13784 s += '</' + nodeName + '>';
13785 }
13786
13787 return s;
13788 }
13789
13790 function getComponentName(component) {
13791 return component.displayName || component !== Function && component.name || getFallbackComponentName(component);
13792 }
13793
13794 function getFallbackComponentName(component) {
13795 var str = Function.prototype.toString.call(component),
13796 name = (str.match(/^\s*function\s+([^\( ]+)/) || EMPTY)[1];
13797 if (!name) {
13798 var index = -1;
13799 for (var i = UNNAMED.length; i--;) {
13800 if (UNNAMED[i] === component) {
13801 index = i;
13802 break;
13803 }
13804 }
13805
13806 if (index < 0) {
13807 index = UNNAMED.push(component) - 1;
13808 }
13809 name = 'UnnamedComponent' + index;
13810 }
13811 return name;
13812 }
13813 renderToString.shallowRender = shallowRender;
13814
13815 return renderToString;
13816
13817 })));
13818
13819
13820 },{}],22:[function(require,module,exports){
13821 !function() {
13822 'use strict';
13823 function VNode() {}
13824 function h(nodeName, attributes) {
13825 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13826 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13827 if (attributes && null != attributes.children) {
13828 if (!stack.length) stack.push(attributes.children);
13829 delete attributes.children;
13830 }
13831 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13832 if ('boolean' == typeof child) child = null;
13833 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13834 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13835 lastSimple = simple;
13836 }
13837 var p = new VNode();
13838 p.nodeName = nodeName;
13839 p.children = children;
13840 p.attributes = null == attributes ? void 0 : attributes;
13841 p.key = null == attributes ? void 0 : attributes.key;
13842 if (void 0 !== options.vnode) options.vnode(p);
13843 return p;
13844 }
13845 function extend(obj, props) {
13846 for (var i in props) obj[i] = props[i];
13847 return obj;
13848 }
13849 function cloneElement(vnode, props) {
13850 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13851 }
13852 function enqueueRender(component) {
13853 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13854 }
13855 function rerender() {
13856 var p, list = items;
13857 items = [];
13858 while (p = list.pop()) if (p.__d) renderComponent(p);
13859 }
13860 function isSameNodeType(node, vnode, hydrating) {
13861 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13862 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13863 }
13864 function isNamedNode(node, nodeName) {
13865 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
13866 }
13867 function getNodeProps(vnode) {
13868 var props = extend({}, vnode.attributes);
13869 props.children = vnode.children;
13870 var defaultProps = vnode.nodeName.defaultProps;
13871 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
13872 return props;
13873 }
13874 function createNode(nodeName, isSvg) {
13875 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
13876 node.__n = nodeName;
13877 return node;
13878 }
13879 function removeNode(node) {
13880 var parentNode = node.parentNode;
13881 if (parentNode) parentNode.removeChild(node);
13882 }
13883 function setAccessor(node, name, old, value, isSvg) {
13884 if ('className' === name) name = 'class';
13885 if ('key' === name) ; else if ('ref' === name) {
13886 if (old) old(null);
13887 if (value) value(node);
13888 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
13889 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
13890 if (value && 'object' == typeof value) {
13891 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
13892 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
13893 }
13894 } else if ('dangerouslySetInnerHTML' === name) {
13895 if (value) node.innerHTML = value.__html || '';
13896 } else if ('o' == name[0] && 'n' == name[1]) {
13897 var useCapture = name !== (name = name.replace(/Capture$/, ''));
13898 name = name.toLowerCase().substring(2);
13899 if (value) {
13900 if (!old) node.addEventListener(name, eventProxy, useCapture);
13901 } else node.removeEventListener(name, eventProxy, useCapture);
13902 (node.__l || (node.__l = {}))[name] = value;
13903 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
13904 setProperty(node, name, null == value ? '' : value);
13905 if (null == value || !1 === value) node.removeAttribute(name);
13906 } else {
13907 var ns = isSvg && name !== (name = name.replace(/^xlink\:?/, ''));
13908 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);
13909 }
13910 }
13911 function setProperty(node, name, value) {
13912 try {
13913 node[name] = value;
13914 } catch (e) {}
13915 }
13916 function eventProxy(e) {
13917 return this.__l[e.type](options.event && options.event(e) || e);
13918 }
13919 function flushMounts() {
13920 var c;
13921 while (c = mounts.pop()) {
13922 if (options.afterMount) options.afterMount(c);
13923 if (c.componentDidMount) c.componentDidMount();
13924 }
13925 }
13926 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
13927 if (!diffLevel++) {
13928 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
13929 hydrating = null != dom && !('__preactattr_' in dom);
13930 }
13931 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
13932 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
13933 if (!--diffLevel) {
13934 hydrating = !1;
13935 if (!componentRoot) flushMounts();
13936 }
13937 return ret;
13938 }
13939 function idiff(dom, vnode, context, mountAll, componentRoot) {
13940 var out = dom, prevSvgMode = isSvgMode;
13941 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
13942 if ('string' == typeof vnode || 'number' == typeof vnode) {
13943 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
13944 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
13945 } else {
13946 out = document.createTextNode(vnode);
13947 if (dom) {
13948 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13949 recollectNodeTree(dom, !0);
13950 }
13951 }
13952 out.__preactattr_ = !0;
13953 return out;
13954 }
13955 var vnodeName = vnode.nodeName;
13956 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
13957 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
13958 vnodeName = String(vnodeName);
13959 if (!dom || !isNamedNode(dom, vnodeName)) {
13960 out = createNode(vnodeName, isSvgMode);
13961 if (dom) {
13962 while (dom.firstChild) out.appendChild(dom.firstChild);
13963 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13964 recollectNodeTree(dom, !0);
13965 }
13966 }
13967 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
13968 if (null == props) {
13969 props = out.__preactattr_ = {};
13970 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
13971 }
13972 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
13973 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
13974 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
13975 diffAttributes(out, vnode.attributes, props);
13976 isSvgMode = prevSvgMode;
13977 return out;
13978 }
13979 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
13980 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;
13981 if (0 !== len) for (var i = 0; i < len; i++) {
13982 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
13983 if (null != key) {
13984 keyedLen++;
13985 keyed[key] = _child;
13986 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
13987 }
13988 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
13989 vchild = vchildren[i];
13990 child = null;
13991 var key = vchild.key;
13992 if (null != key) {
13993 if (keyedLen && void 0 !== keyed[key]) {
13994 child = keyed[key];
13995 keyed[key] = void 0;
13996 keyedLen--;
13997 }
13998 } else if (!child && min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
13999 child = c;
14000 children[j] = void 0;
14001 if (j === childrenLen - 1) childrenLen--;
14002 if (j === min) min++;
14003 break;
14004 }
14005 child = idiff(child, vchild, context, mountAll);
14006 f = originalChildren[i];
14007 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
14008 }
14009 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
14010 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
14011 }
14012 function recollectNodeTree(node, unmountOnly) {
14013 var component = node._component;
14014 if (component) unmountComponent(component); else {
14015 if (null != node.__preactattr_ && node.__preactattr_.ref) node.__preactattr_.ref(null);
14016 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
14017 removeChildren(node);
14018 }
14019 }
14020 function removeChildren(node) {
14021 node = node.lastChild;
14022 while (node) {
14023 var next = node.previousSibling;
14024 recollectNodeTree(node, !0);
14025 node = next;
14026 }
14027 }
14028 function diffAttributes(dom, attrs, old) {
14029 var name;
14030 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
14031 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);
14032 }
14033 function collectComponent(component) {
14034 var name = component.constructor.name;
14035 (components[name] || (components[name] = [])).push(component);
14036 }
14037 function createComponent(Ctor, props, context) {
14038 var inst, list = components[Ctor.name];
14039 if (Ctor.prototype && Ctor.prototype.render) {
14040 inst = new Ctor(props, context);
14041 Component.call(inst, props, context);
14042 } else {
14043 inst = new Component(props, context);
14044 inst.constructor = Ctor;
14045 inst.render = doRender;
14046 }
14047 if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) {
14048 inst.__b = list[i].__b;
14049 list.splice(i, 1);
14050 break;
14051 }
14052 return inst;
14053 }
14054 function doRender(props, state, context) {
14055 return this.constructor(props, context);
14056 }
14057 function setComponentProps(component, props, opts, context, mountAll) {
14058 if (!component.__x) {
14059 component.__x = !0;
14060 if (component.__r = props.ref) delete props.ref;
14061 if (component.__k = props.key) delete props.key;
14062 if (!component.base || mountAll) {
14063 if (component.componentWillMount) component.componentWillMount();
14064 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
14065 if (context && context !== component.context) {
14066 if (!component.__c) component.__c = component.context;
14067 component.context = context;
14068 }
14069 if (!component.__p) component.__p = component.props;
14070 component.props = props;
14071 component.__x = !1;
14072 if (0 !== opts) if (1 === opts || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
14073 if (component.__r) component.__r(component);
14074 }
14075 }
14076 function renderComponent(component, opts, mountAll, isChild) {
14077 if (!component.__x) {
14078 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;
14079 if (isUpdate) {
14080 component.props = previousProps;
14081 component.state = previousState;
14082 component.context = previousContext;
14083 if (2 !== opts && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
14084 component.props = props;
14085 component.state = state;
14086 component.context = context;
14087 }
14088 component.__p = component.__s = component.__c = component.__b = null;
14089 component.__d = !1;
14090 if (!skip) {
14091 rendered = component.render(props, state, context);
14092 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
14093 var toUnmount, base, childComponent = rendered && rendered.nodeName;
14094 if ('function' == typeof childComponent) {
14095 var childProps = getNodeProps(rendered);
14096 inst = initialChildComponent;
14097 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
14098 toUnmount = inst;
14099 component._component = inst = createComponent(childComponent, childProps, context);
14100 inst.__b = inst.__b || nextBase;
14101 inst.__u = component;
14102 setComponentProps(inst, childProps, 0, context, !1);
14103 renderComponent(inst, 1, mountAll, !0);
14104 }
14105 base = inst.base;
14106 } else {
14107 cbase = initialBase;
14108 toUnmount = initialChildComponent;
14109 if (toUnmount) cbase = component._component = null;
14110 if (initialBase || 1 === opts) {
14111 if (cbase) cbase._component = null;
14112 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
14113 }
14114 }
14115 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
14116 var baseParent = initialBase.parentNode;
14117 if (baseParent && base !== baseParent) {
14118 baseParent.replaceChild(base, initialBase);
14119 if (!toUnmount) {
14120 initialBase._component = null;
14121 recollectNodeTree(initialBase, !1);
14122 }
14123 }
14124 }
14125 if (toUnmount) unmountComponent(toUnmount);
14126 component.base = base;
14127 if (base && !isChild) {
14128 var componentRef = component, t = component;
14129 while (t = t.__u) (componentRef = t).base = base;
14130 base._component = componentRef;
14131 base._componentConstructor = componentRef.constructor;
14132 }
14133 }
14134 if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) {
14135 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext);
14136 if (options.afterUpdate) options.afterUpdate(component);
14137 }
14138 if (null != component.__h) while (component.__h.length) component.__h.pop().call(component);
14139 if (!diffLevel && !isChild) flushMounts();
14140 }
14141 }
14142 function buildComponentFromVNode(dom, vnode, context, mountAll) {
14143 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
14144 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
14145 if (c && isOwner && (!mountAll || c._component)) {
14146 setComponentProps(c, props, 3, context, mountAll);
14147 dom = c.base;
14148 } else {
14149 if (originalComponent && !isDirectOwner) {
14150 unmountComponent(originalComponent);
14151 dom = oldDom = null;
14152 }
14153 c = createComponent(vnode.nodeName, props, context);
14154 if (dom && !c.__b) {
14155 c.__b = dom;
14156 oldDom = null;
14157 }
14158 setComponentProps(c, props, 1, context, mountAll);
14159 dom = c.base;
14160 if (oldDom && dom !== oldDom) {
14161 oldDom._component = null;
14162 recollectNodeTree(oldDom, !1);
14163 }
14164 }
14165 return dom;
14166 }
14167 function unmountComponent(component) {
14168 if (options.beforeUnmount) options.beforeUnmount(component);
14169 var base = component.base;
14170 component.__x = !0;
14171 if (component.componentWillUnmount) component.componentWillUnmount();
14172 component.base = null;
14173 var inner = component._component;
14174 if (inner) unmountComponent(inner); else if (base) {
14175 if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null);
14176 component.__b = base;
14177 removeNode(base);
14178 collectComponent(component);
14179 removeChildren(base);
14180 }
14181 if (component.__r) component.__r(null);
14182 }
14183 function Component(props, context) {
14184 this.__d = !0;
14185 this.context = context;
14186 this.props = props;
14187 this.state = this.state || {};
14188 }
14189 function render(vnode, parent, merge) {
14190 return diff(merge, vnode, {}, !1, parent, !1);
14191 }
14192 var options = {};
14193 var stack = [];
14194 var EMPTY_CHILDREN = [];
14195 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14196 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14197 var items = [];
14198 var mounts = [];
14199 var diffLevel = 0;
14200 var isSvgMode = !1;
14201 var hydrating = !1;
14202 var components = {};
14203 extend(Component.prototype, {
14204 setState: function(state, callback) {
14205 var s = this.state;
14206 if (!this.__s) this.__s = extend({}, s);
14207 extend(s, 'function' == typeof state ? state(s, this.props) : state);
14208 if (callback) (this.__h = this.__h || []).push(callback);
14209 enqueueRender(this);
14210 },
14211 forceUpdate: function(callback) {
14212 if (callback) (this.__h = this.__h || []).push(callback);
14213 renderComponent(this, 2);
14214 },
14215 render: function() {}
14216 });
14217 var preact = {
14218 h: h,
14219 createElement: h,
14220 cloneElement: cloneElement,
14221 Component: Component,
14222 render: render,
14223 rerender: rerender,
14224 options: options
14225 };
14226 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14227 }();
14228
14229 },{}],23:[function(require,module,exports){
14230 function tlite(getTooltipOpts) {
14231 document.addEventListener('mouseover', function (e) {
14232 var el = e.target;
14233 var opts = getTooltipOpts(el);
14234
14235 if (!opts) {
14236 el = el.parentElement;
14237 opts = el && getTooltipOpts(el);
14238 }
14239
14240 opts && tlite.show(el, opts, true);
14241 });
14242 }
14243
14244 tlite.show = function (el, opts, isAuto) {
14245 var fallbackAttrib = 'data-tlite';
14246 opts = opts || {};
14247
14248 (el.tooltip || Tooltip(el, opts)).show();
14249
14250 function Tooltip(el, opts) {
14251 var tooltipEl;
14252 var showTimer;
14253 var text;
14254
14255 el.addEventListener('mousedown', autoHide);
14256 el.addEventListener('mouseleave', autoHide);
14257
14258 function show() {
14259 text = el.title || el.getAttribute(fallbackAttrib) || text;
14260 el.title = '';
14261 el.setAttribute(fallbackAttrib, '');
14262 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
14263 }
14264
14265 function autoHide() {
14266 tlite.hide(el, true);
14267 }
14268
14269 function hide(isAutoHiding) {
14270 if (isAuto === isAutoHiding) {
14271 showTimer = clearTimeout(showTimer);
14272 var parent = tooltipEl && tooltipEl.parentNode;
14273 parent && parent.removeChild(tooltipEl);
14274 tooltipEl = undefined;
14275 }
14276 }
14277
14278 function fadeIn() {
14279 if (!tooltipEl) {
14280 tooltipEl = createTooltip(el, text, opts);
14281 }
14282 }
14283
14284 return el.tooltip = {
14285 show: show,
14286 hide: hide
14287 };
14288 }
14289
14290 function createTooltip(el, text, opts) {
14291 var tooltipEl = document.createElement('span');
14292 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
14293
14294 tooltipEl.innerHTML = text;
14295
14296 el.appendChild(tooltipEl);
14297
14298 var vertGrav = grav[0] || '';
14299 var horzGrav = grav[1] || '';
14300
14301 function positionTooltip() {
14302 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
14303
14304 var arrowSize = 10;
14305 var top = el.offsetTop;
14306 var left = el.offsetLeft;
14307
14308 if (tooltipEl.offsetParent === el) {
14309 top = left = 0;
14310 }
14311
14312 var width = el.offsetWidth;
14313 var height = el.offsetHeight;
14314 var tooltipHeight = tooltipEl.offsetHeight;
14315 var tooltipWidth = tooltipEl.offsetWidth;
14316 var centerEl = left + (width / 2);
14317
14318 tooltipEl.style.top = (
14319 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
14320 vertGrav === 'n' ? (top + height + arrowSize) :
14321 (top + (height / 2) - (tooltipHeight / 2))
14322 ) + 'px';
14323
14324 tooltipEl.style.left = (
14325 horzGrav === 'w' ? left :
14326 horzGrav === 'e' ? left + width - tooltipWidth :
14327 vertGrav === 'w' ? (left + width + arrowSize) :
14328 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
14329 (centerEl - tooltipWidth / 2)
14330 ) + 'px';
14331 }
14332
14333 positionTooltip();
14334
14335 var rect = tooltipEl.getBoundingClientRect();
14336
14337 if (vertGrav === 's' && rect.top < 0) {
14338 vertGrav = 'n';
14339 positionTooltip();
14340 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
14341 vertGrav = 's';
14342 positionTooltip();
14343 } else if (vertGrav === 'e' && rect.left < 0) {
14344 vertGrav = 'w';
14345 positionTooltip();
14346 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
14347 vertGrav = 'e';
14348 positionTooltip();
14349 }
14350
14351 tooltipEl.className += ' tlite-visible';
14352
14353 return tooltipEl;
14354 }
14355 };
14356
14357 tlite.hide = function (el, isAuto) {
14358 el.tooltip && el.tooltip.hide(isAuto);
14359 };
14360
14361 if (typeof module !== 'undefined' && module.exports) {
14362 module.exports = tlite;
14363 }
14364
14365 },{}]},{},[2]);
14366 ; })();