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

13,878 lines 555.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined;(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2 "use strict";
3
4 Object.defineProperty(exports, "__esModule", {
5 value: true
6 });
7 exports["default"] = void 0;
8
9 function init() {
10 document.body.addEventListener('click', handleClickEvent, true);
11 }
12
13 function handleClickEvent(e) {
14 if (e.target.tagName !== 'A') {
15 return;
16 }
17
18 if (e.target.hasAttribute('data-hf-confirm')) {
19 var sure = confirm(e.target.getAttribute('data-hf-confirm'));
20
21 if (!sure) {
22 e.preventDefault();
23 }
24 }
25 }
26
27 var _default = {
28 init: init
29 };
30 exports["default"] = _default;
31
32 },{}],2:[function(require,module,exports){
33 "use strict";
34
35 var _tabs = _interopRequireDefault(require("./tabs.js"));
36
37 var _formEditor = _interopRequireDefault(require("./form-editor.js"));
38
39 var _formActions = _interopRequireDefault(require("./form-actions.js"));
40
41 var _fieldBuilder = _interopRequireDefault(require("./field-builder.js"));
42
43 var _actionConfirmations = _interopRequireDefault(require("./action-confirmations.js"));
44
45 var _tlite = _interopRequireDefault(require("tlite"));
46
47 var _preact = require("preact");
48
49 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
50
51 window.html_forms = {}; // init the various components
52
53 _tabs["default"].init();
54
55 _actionConfirmations["default"].init();
56
57 if (document.getElementById('hf-form-editor')) {
58 _formEditor["default"].init();
59
60 _formActions["default"].init();
61
62 _fieldBuilder["default"].init(_formEditor["default"]);
63 }
64
65 (0, _tlite["default"])(function (el) {
66 return el.className.indexOf('hf-tooltip') > -1;
67 });
68 window.html_forms.FieldBuilder = _fieldBuilder["default"];
69 window.html_forms.Editor = _formEditor["default"]; // tell WP common.js to override the method used for determining hidden columns (screen options)
70
71 if (window.hf_options.view === 'edit') {
72 window.columns.useCheckboxesForHidden();
73 }
74
75 },{"./action-confirmations.js":1,"./field-builder.js":6,"./form-actions.js":8,"./form-editor.js":9,"./tabs.js":10,"preact":21,"tlite":22}],3:[function(require,module,exports){
76 "use strict";
77
78 Object.defineProperty(exports, "__esModule", {
79 value: true
80 });
81 exports.FieldBuilder = void 0;
82
83 var _preact = require("preact");
84
85 var _fieldConfigurator = require("./field-configurator.js");
86
87 function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
88
89 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
90
91 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
92
93 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
94
95 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
96
97 function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
98
99 function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
100
101 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
102
103 function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
104
105 function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
106
107 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
108
109 var FieldBuilder = /*#__PURE__*/function (_Component) {
110 _inherits(FieldBuilder, _Component);
111
112 var _super = _createSuper(FieldBuilder);
113
114 function FieldBuilder(props) {
115 var _this;
116
117 _classCallCheck(this, FieldBuilder);
118
119 _this = _super.call(this, props);
120 _this.state = {
121 activeField: null
122 };
123 _this.handleCancel = _this.handleCancel.bind(_assertThisInitialized(_this));
124 _this.openFieldConfig = _this.openFieldConfig.bind(_assertThisInitialized(_this));
125 return _this;
126 }
127
128 _createClass(FieldBuilder, [{
129 key: "handleCancel",
130 value: function handleCancel() {
131 this.setState({
132 activeField: null
133 });
134 }
135 }, {
136 key: "openFieldConfig",
137 value: function openFieldConfig(e) {
138 var field = this.props.fields[e.target.value];
139
140 if (this.state.activeField === field) {
141 this.setState({
142 activeField: null
143 });
144 } else {
145 this.setState({
146 activeField: field
147 });
148 }
149 }
150 }, {
151 key: "render",
152 value: function render(props, state) {
153 var _this2 = this;
154
155 var fieldButtons = props.fields.map(function (f, i) {
156 return (0, _preact.h)("button", {
157 type: "button",
158 value: i,
159 className: 'button ' + (state.activeField === f ? 'active' : ''),
160 onClick: _this2.openFieldConfig
161 }, f.label);
162 });
163 var fieldType = state.activeField ? state.activeField.key : '';
164 var rows = state.activeField ? state.activeField.configRows : [];
165 return (0, _preact.h)("div", {
166 "class": "hf-field-builder"
167 }, (0, _preact.h)("h4", null, "Add field"), (0, _preact.h)("div", {
168 "class": "available-fields"
169 }, fieldButtons), (0, _preact.h)("div", {
170 style: "max-width: 480px;"
171 }, (0, _preact.h)(_fieldConfigurator.FieldConfigurator, {
172 fieldType: fieldType,
173 rows: rows,
174 onCancel: this.handleCancel
175 })), state.activeField === null ? (0, _preact.h)("p", {
176 "class": "help",
177 style: "margin-bottom: 0;"
178 }, "Use the buttons above to generate your field HTML, or manually modify your form below.") : '');
179 }
180 }]);
181
182 return FieldBuilder;
183 }(_preact.Component);
184
185 exports.FieldBuilder = FieldBuilder;
186
187 },{"./field-configurator.js":4,"preact":21}],4:[function(require,module,exports){
188 "use strict";
189
190 Object.defineProperty(exports, "__esModule", {
191 value: true
192 });
193 exports.FieldConfigurator = void 0;
194
195 var _preact = require("preact");
196
197 var _html = require("../field-builder/html.js");
198
199 var FS = _interopRequireWildcard(require("./field-settings.js"));
200
201 var _linkstate = _interopRequireDefault(require("linkstate"));
202
203 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
204
205 function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
206
207 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
208
209 function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
210
211 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
212
213 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); } }
214
215 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
216
217 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
218
219 function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
220
221 function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
222
223 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
224
225 function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
226
227 function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
228
229 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
230
231 var FieldConfigurator = /*#__PURE__*/function (_Component) {
232 _inherits(FieldConfigurator, _Component);
233
234 var _super = _createSuper(FieldConfigurator);
235
236 function FieldConfigurator(props) {
237 var _this;
238
239 _classCallCheck(this, FieldConfigurator);
240
241 _this = _super.call(this, props);
242 _this.state = _this.getInitialState();
243 _this.choiceHandlers = {
244 add: _this.addChoice.bind(_assertThisInitialized(_this)),
245 "delete": _this.deleteChoice.bind(_assertThisInitialized(_this)),
246 changeLabel: _this.changeChoiceLabel.bind(_assertThisInitialized(_this)),
247 toggleChecked: _this.toggleChoiceChecked.bind(_assertThisInitialized(_this))
248 };
249 _this.addToForm = _this.addToForm.bind(_assertThisInitialized(_this));
250 _this.handleCancel = _this.handleCancel.bind(_assertThisInitialized(_this));
251 return _this;
252 }
253
254 _createClass(FieldConfigurator, [{
255 key: "getInitialState",
256 value: function getInitialState() {
257 return {
258 formId: document.querySelector('input[name="form_id"]').value,
259 formSlug: document.querySelector('input[name="form[slug]"]').value,
260 fieldType: '',
261 fieldLabel: '',
262 placeholder: '',
263 value: '',
264 multiple: false,
265 wrap: true,
266 required: false,
267 choices: [{
268 checked: false,
269 label: 'One'
270 }, {
271 checked: false,
272 label: 'Two'
273 }],
274 accept: ''
275 };
276 }
277 }, {
278 key: "componentWillReceiveProps",
279 value: function componentWillReceiveProps(props) {
280 var newState = {
281 fieldType: props.fieldType
282 }; // when changing from field that accepts multiple values to single-value field, reset all pre-selections
283
284 if (this.state.fieldType === 'checkbox' && props.fieldType !== 'checkbox') {
285 newState.choices = this.state.choices.map(function (c, i) {
286 c.checked = false;
287 return c;
288 });
289 }
290
291 this.setState(newState);
292 }
293 }, {
294 key: "addToForm",
295 value: function addToForm() {
296 var html = (0, _html.htmlgenerate)(this.state);
297 window.html_forms.Editor.replaceSelection(html);
298 }
299 }, {
300 key: "addChoice",
301 value: function addChoice() {
302 var arr = this.state.choices;
303 arr.push({
304 checked: false,
305 label: '...'
306 });
307 this.setState({
308 choices: arr
309 });
310 }
311 }, {
312 key: "deleteChoice",
313 value: function deleteChoice(e) {
314 var arr = this.state.choices;
315 var index = e.target.parentElement.getAttribute('data-key');
316 arr.splice(index, 1);
317 this.setState({
318 choices: arr
319 });
320 }
321 }, {
322 key: "changeChoiceLabel",
323 value: function changeChoiceLabel(e) {
324 var arr = this.state.choices;
325 var index = e.target.parentElement.getAttribute('data-key');
326 arr[index].label = e.target.value;
327 this.setState({
328 choices: arr
329 });
330 }
331 }, {
332 key: "toggleChoiceChecked",
333 value: function toggleChoiceChecked(e) {
334 var arr = this.state.choices;
335 var index = e.target.parentElement.getAttribute('data-key');
336 arr[index].checked = !arr[index].checked;
337 this.setState({
338 choices: arr
339 });
340 }
341 }, {
342 key: "handleCancel",
343 value: function handleCancel() {
344 // revert back to initial state
345 this.setState(this.getInitialState());
346 this.props.onCancel();
347 }
348 }, {
349 key: "render",
350 value: function render(props, state) {
351 if (props.rows.length === 0) {
352 return '';
353 }
354
355 var formFields = [];
356
357 for (var i = 0; i < props.rows.length; i++) {
358 switch (props.rows[i]) {
359 case 'label':
360 formFields.push((0, _preact.h)(FS.Label, {
361 value: state.fieldLabel,
362 onChange: (0, _linkstate["default"])(this, 'fieldLabel')
363 }));
364 break;
365
366 case 'placeholder':
367 formFields.push((0, _preact.h)(FS.Placeholder, {
368 value: state.placeholder,
369 onChange: (0, _linkstate["default"])(this, 'placeholder')
370 }));
371 break;
372
373 case 'default-value':
374 formFields.push((0, _preact.h)(FS.DefaultValue, {
375 value: state.value,
376 onChange: (0, _linkstate["default"])(this, 'value')
377 }));
378 break;
379
380 case 'multiple':
381 formFields.push((0, _preact.h)(FS.Multiple, {
382 checked: state.multiple,
383 onChange: (0, _linkstate["default"])(this, 'multiple')
384 }));
385 break;
386
387 case 'required':
388 formFields.push((0, _preact.h)(FS.Required, {
389 checked: state.required,
390 onChange: (0, _linkstate["default"])(this, 'required')
391 }));
392 break;
393
394 case 'wrap':
395 formFields.push((0, _preact.h)(FS.Wrap, {
396 checked: state.wrap,
397 onChange: (0, _linkstate["default"])(this, 'wrap')
398 }));
399 break;
400
401 case 'add-to-form':
402 formFields.push((0, _preact.h)(FS.AddToForm, {
403 onSubmit: this.addToForm,
404 onCancel: this.handleCancel
405 }));
406 break;
407
408 case 'choices':
409 formFields.push((0, _preact.h)(FS.Choices, {
410 multiple: state.fieldType === 'checkbox',
411 choices: state.choices,
412 handlers: this.choiceHandlers
413 }));
414 break;
415
416 case 'button-text':
417 formFields.push((0, _preact.h)(FS.ButtonText, {
418 value: state.value,
419 onChange: (0, _linkstate["default"])(this, 'value')
420 }));
421 break;
422
423 case 'accept':
424 formFields.push((0, _preact.h)(FS.Accept, {
425 value: state.accept,
426 onChange: (0, _linkstate["default"])(this, 'accept')
427 }));
428 break;
429 }
430 }
431
432 return (0, _preact.h)("div", {
433 "class": "field-config",
434 onKeyPress: FieldConfigurator.handleKeyPress
435 }, formFields);
436 }
437 }], [{
438 key: "handleKeyPress",
439 value: function handleKeyPress(e) {
440 // stop RETURN from submitting the parent form.
441 if (e.keyCode === 13) {
442 e.preventDefault();
443 }
444 }
445 }]);
446
447 return FieldConfigurator;
448 }(_preact.Component);
449
450 exports.FieldConfigurator = FieldConfigurator;
451
452 },{"../field-builder/html.js":7,"./field-settings.js":5,"linkstate":19,"preact":21}],5:[function(require,module,exports){
453 "use strict";
454
455 Object.defineProperty(exports, "__esModule", {
456 value: true
457 });
458 exports.AddToForm = AddToForm;
459 exports.Label = Label;
460 exports.Placeholder = Placeholder;
461 exports.DefaultValue = DefaultValue;
462 exports.Wrap = Wrap;
463 exports.Required = Required;
464 exports.Choices = Choices;
465 exports.ButtonText = ButtonText;
466 exports.Accept = Accept;
467 exports.Multiple = Multiple;
468
469 var _preact = require("preact");
470
471 function AddToForm(props) {
472 return (0, _preact.h)("div", {
473 "class": "hf-small-margin"
474 }, (0, _preact.h)("button", {
475 "class": "button",
476 type: "button",
477 onClick: props.onSubmit
478 }, "Add field to form"), " \xA0 ", (0, _preact.h)("a", {
479 href: "javascript:void(0);",
480 "class": "hf-small",
481 style: "vertical-align: middle;",
482 onClick: props.onCancel
483 }, "or close field helper"));
484 }
485
486 function Label(props) {
487 return (0, _preact.h)("div", {
488 "class": "hf-small-margin"
489 }, (0, _preact.h)("label", {
490 "for": "hf-fg-field-label"
491 }, "Field label ", (0, _preact.h)("span", {
492 "class": "hf-required"
493 }, "*")), (0, _preact.h)("input", {
494 id: "hf-fg-field-label",
495 type: "text",
496 value: props.value,
497 onChange: props.onChange
498 }));
499 }
500
501 function Placeholder(props) {
502 return (0, _preact.h)("div", {
503 "class": "hf-small-margin"
504 }, (0, _preact.h)("label", {
505 "for": "hf-fg-placeholder"
506 }, "Placeholder ", (0, _preact.h)("span", {
507 "class": "hf-italic hf-pull-right"
508 }, "Optional")), (0, _preact.h)("input", {
509 id: "hf-fg-placeholder",
510 type: "text",
511 value: props.value,
512 onChange: props.onChange
513 }), (0, _preact.h)("p", {
514 "class": "help"
515 }, "Text to show when field has no value."));
516 }
517
518 function ButtonText(props) {
519 return (0, _preact.h)("div", {
520 "class": "hf-small-margin"
521 }, (0, _preact.h)("label", {
522 "for": "hf-fg-default-value"
523 }, "Button text ", (0, _preact.h)("span", {
524 "class": "hf-required"
525 }, "*")), (0, _preact.h)("input", {
526 id: "hf-fg-default-value",
527 type: "text",
528 value: props.value,
529 onChange: props.onChange
530 }), (0, _preact.h)("p", {
531 "class": "help"
532 }, "Text to show on the button."));
533 }
534
535 function DefaultValue(props) {
536 return (0, _preact.h)("div", {
537 "class": "hf-small-margin"
538 }, (0, _preact.h)("label", {
539 "for": "hf-fg-default-value"
540 }, "Default value ", (0, _preact.h)("span", {
541 "class": "hf-italic hf-pull-right"
542 }, "Optional")), (0, _preact.h)("input", {
543 id: "hf-fg-default-value",
544 type: "text",
545 value: props.value,
546 onChange: props.onChange
547 }), (0, _preact.h)("p", {
548 "class": "help"
549 }, "Text to pre-fill this field with."));
550 }
551
552 function Multiple(props) {
553 return (0, _preact.h)("div", {
554 "class": "hf-small-margin"
555 }, (0, _preact.h)("label", {
556 "class": "inline"
557 }, (0, _preact.h)("input", {
558 type: "checkbox",
559 value: "1",
560 defaultChecked: props.checked,
561 onChange: props.onChange
562 }), "Accept multiple values."));
563 }
564
565 function Wrap(props) {
566 return (0, _preact.h)("div", {
567 "class": "hf-small-margin"
568 }, (0, _preact.h)("label", {
569 "class": "inline"
570 }, (0, _preact.h)("input", {
571 type: "checkbox",
572 value: "1",
573 defaultChecked: props.checked,
574 onChange: props.onChange
575 }), "Wrap this field in paragraph tags."));
576 }
577
578 function Required(props) {
579 return (0, _preact.h)("div", {
580 "class": "hf-small-margin"
581 }, (0, _preact.h)("label", {
582 "class": "inline"
583 }, (0, _preact.h)("input", {
584 type: "checkbox",
585 value: "1",
586 defaultChecked: props.checked,
587 onChange: props.onChange
588 }), "This field is required."));
589 }
590
591 function Choices(props) {
592 var choiceFields = props.choices.map(function (choice, k) {
593 return (0, _preact.h)("div", {
594 "data-key": k
595 }, (0, _preact.h)("input", {
596 type: props.multiple ? 'checkbox' : 'radio',
597 name: "selected",
598 defaultChecked: choice.checked,
599 onChange: props.handlers.toggleChecked,
600 title: "Pre-select this choice?"
601 }), (0, _preact.h)("input", {
602 type: "text",
603 value: choice.label,
604 placeholder: "Choice label",
605 style: "width: 80%;",
606 onChange: props.handlers.changeLabel
607 }), (0, _preact.h)("a", {
608 href: "javascript:void(0);",
609 onClick: props.handlers["delete"],
610 style: "text-decoration: none;",
611 title: "Delete choice"
612 }, "\u2715"));
613 });
614 return (0, _preact.h)("div", {
615 "class": "hf-small-margin"
616 }, (0, _preact.h)("label", null, "Choices"), choiceFields, (0, _preact.h)("input", {
617 type: props.multiple ? 'checkbox' : 'radio',
618 style: "visibility: hidden;"
619 }), (0, _preact.h)("a", {
620 href: "javascript:void(0);",
621 onClick: props.handlers.add
622 }, "Add choice"));
623 }
624
625 function Accept(props) {
626 return (0, _preact.h)("div", {
627 "class": "hf-small-margin"
628 }, (0, _preact.h)("label", null, "Accepted file types"), (0, _preact.h)("input", {
629 type: "text",
630 value: props.value,
631 onChange: props.onChange
632 }), (0, _preact.h)("p", {
633 "class": "help"
634 }, "Use a comma-separated list of accepted file extensions, eg ", (0, _preact.h)("code", null, ".pdf"), ". ", (0, _preact.h)("br", null), "Leave empty to accept any file type."));
635 }
636
637 },{"preact":21}],6:[function(require,module,exports){
638 "use strict";
639
640 Object.defineProperty(exports, "__esModule", {
641 value: true
642 });
643 exports["default"] = void 0;
644
645 var _preact = require("preact");
646
647 var _fieldBuilder = require("./components/field-builder.js");
648
649 // imports
650 // vars
651 var rootElement; // functions
652
653 function Field(key, label, configRows) {
654 this.key = key;
655 this.label = label;
656 this.configRows = configRows || [];
657 }
658
659 function mount() {
660 rootElement = (0, _preact.render)((0, _preact.h)(_fieldBuilder.FieldBuilder, {
661 fields: fields
662 }), document.getElementById('hf-field-builder'), rootElement);
663 } // bootstrap
664
665
666 var fields = [new Field('text', 'Text', ['label', 'placeholder', 'default-value', 'required', 'wrap', 'add-to-form']), new Field('email', 'Email', ['label', 'placeholder', 'default-value', 'required', 'wrap', 'add-to-form']), new Field('url', 'URL', ['label', 'placeholder', 'default-value', 'required', 'wrap', 'add-to-form']), new Field('number', 'Number', ['label', 'placeholder', 'default-value', 'required', 'wrap', 'add-to-form']), new Field('date', 'Date', ['label', 'default-value', 'required', 'wrap', 'add-to-form']), new Field('textarea', 'Textarea', ['label', 'placeholder', 'default-value', 'required', 'wrap', 'add-to-form']), new Field('dropdown', 'Dropdown', ['label', 'choices', 'multiple', 'required', 'wrap', 'add-to-form']), new Field('checkbox', 'Checkboxes', ['label', 'choices', 'wrap', 'add-to-form']), new Field('radio', 'Radio buttons', ['label', 'choices', 'wrap', 'add-to-form']), new Field('submit', 'Submit button', ['button-text', 'wrap', 'add-to-form'])];
667 var _default = {
668 init: function init() {
669 mount();
670 },
671 registerField: function registerField(key, label, configRows) {
672 fields.push(new Field(key, label, configRows));
673 mount();
674 }
675 };
676 exports["default"] = _default;
677
678 },{"./components/field-builder.js":3,"preact":21}],7:[function(require,module,exports){
679 'use strict';
680
681 Object.defineProperty(exports, "__esModule", {
682 value: true
683 });
684 exports.htmlgenerate = htmlgenerate;
685
686 var _preact = require("preact");
687
688 var _preactRenderToString = _interopRequireDefault(require("preact-render-to-string"));
689
690 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
691
692 function htmlgenerate(conf) {
693 var fieldName = namify(conf.fieldLabel);
694 var fieldId = conf.formSlug + '-' + fieldName;
695 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)('label', {
696 "for": fieldId
697 }, conf.fieldLabel) : '';
698 var fieldAttr, field;
699
700 switch (conf.fieldType) {
701 case 'text':
702 default:
703 fieldAttr = {
704 type: conf.fieldType,
705 name: fieldName,
706 value: conf.value,
707 placeholder: conf.placeholder,
708 required: conf.required,
709 id: fieldId
710 };
711 field = html('input', fieldAttr);
712 break;
713
714 case 'textarea':
715 fieldAttr = {
716 name: fieldName,
717 placeholder: conf.placeholder,
718 required: conf.required,
719 id: fieldId
720 };
721 field = html('textarea', fieldAttr, conf.value);
722 break;
723
724 case 'dropdown':
725 fieldAttr = {
726 name: fieldName + (conf.multiple ? '[]' : ''),
727 required: conf.required,
728 multiple: conf.multiple,
729 id: fieldId
730 };
731 field = html('select', fieldAttr, conf.choices.map(function (choice) {
732 return html('option', {
733 selected: choice.checked
734 }, choice.label);
735 }));
736 break;
737
738 case 'radio':
739 field = conf.choices.map(function (choice) {
740 return html('label', {}, [html('input', {
741 type: 'radio',
742 name: fieldName,
743 value: choice.label,
744 selected: choice.checked
745 }), ' ', html('span', {}, choice.label)]);
746 });
747 break;
748
749 case 'checkbox':
750 field = conf.choices.map(function (choice) {
751 return html('label', {}, [html('input', {
752 type: 'checkbox',
753 name: fieldName + '[]',
754 value: choice.label,
755 checked: choice.checked
756 }), ' ', html('span', {}, choice.label)]);
757 });
758 break;
759
760 case 'file':
761 fieldAttr = {
762 type: 'file',
763 name: fieldName,
764 required: conf.required,
765 id: fieldId
766 };
767
768 if (conf.accept) {
769 fieldAttr.accept = conf.accept;
770 }
771
772 field = html('input', fieldAttr);
773 break;
774
775 case 'submit':
776 fieldAttr = {
777 type: 'submit',
778 value: conf.value
779 };
780 field = html('input', fieldAttr);
781 break;
782 }
783
784 var str = '';
785
786 if (conf.wrap) {
787 var children = label !== '' ? ['\n\t', label, '\n\t', field, '\n'] : ['\n\t', field, '\n'];
788 var tmpl = (0, _preact.h)('p', {}, children);
789 str = renderToString(tmpl);
790 } else {
791 var _children = label !== '' ? [label, '\n', field] : [field];
792
793 str = renderToString(_children);
794 }
795
796 return str;
797 }
798
799 function renderToString(vdom) {
800 return (0, _preactRenderToString["default"])(vdom, {
801 pretty: true
802 });
803 }
804
805 function html(tag, attr, children) {
806 attr = filterEmptyObjectValues(attr);
807 return (0, _preact.h)(tag, attr, children);
808 }
809
810 function namify(str) {
811 return str.replace(/ /g, '_').replace(/[^\w[\]_]*/g, '').toUpperCase();
812 }
813
814 function filterEmptyObjectValues(obj) {
815 var newObj = {};
816
817 for (var propName in obj) {
818 if (obj[propName] !== false && obj[propName] !== '') {
819 newObj[propName] = obj[propName];
820 }
821 }
822
823 return newObj;
824 }
825
826 },{"preact":21,"preact-render-to-string":20}],8:[function(require,module,exports){
827 "use strict";
828
829 Object.defineProperty(exports, "__esModule", {
830 value: true
831 });
832 exports["default"] = void 0;
833 var availableActions, actionTemplates, actions;
834
835 function init() {
836 actions = document.getElementById('hf-form-actions');
837 availableActions = document.getElementById('hf-available-form-actions');
838 actionTemplates = document.getElementById('hf-form-action-templates'); // turn settings into accordions
839
840 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
841 el.parentNode.removeChild(el);
842 var heading = el.getAttribute('data-title');
843 var summary = el.querySelector('.hf-action-summary');
844
845 if (summary) {
846 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
847 }
848
849 var wrap = createAccordion(heading, el.innerHTML);
850 actions.appendChild(wrap);
851 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
852 });
853 availableActions.addEventListener('click', addAction, true);
854 }
855
856 function createAccordion(headingHTML, contentHTML) {
857 var wrap = document.createElement('div');
858 wrap.className = 'hf-accordion expanded ';
859 var heading = document.createElement('h4');
860 heading.className = 'hf-accordion-heading';
861 heading.innerHTML = headingHTML;
862 wrap.appendChild(heading);
863 var content = document.createElement('div');
864 content.className = 'hf-accordion-content';
865 content.innerHTML = contentHTML;
866 wrap.appendChild(content);
867 var deleteWrap = document.createElement('p');
868 deleteWrap.style.textAlign = 'right';
869 var deleteLink = document.createElement('a');
870 deleteLink.href = 'javascript:void(0);';
871 deleteLink.className = 'danger';
872 deleteLink.innerText = 'Delete this action';
873 deleteWrap.appendChild(deleteLink);
874 content.appendChild(deleteWrap); // bind handlers
875
876 heading.addEventListener('click', createToggleActionHandler(wrap, content));
877 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
878 return wrap;
879 }
880
881 function addAction(e) {
882 var el = e.target || e.srcElement;
883
884 if (el.tagName !== 'INPUT') {
885 return;
886 }
887
888 var actionType = el.getAttribute('data-action-type');
889 var actionTemplate = actionTemplates.querySelector("#hf-action-type-".concat(actionType, "-template")); // append HTML to actions wrapper
890
891 var index = actions.querySelectorAll('div').length - 1;
892 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
893 actions.appendChild(wrap); // hide "no form actions" message
894
895 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
896 window.html_forms.Editor.updateFieldVariables();
897 }
898
899 function createDeleteActionHandler(wrap) {
900 return function () {
901 actions.removeChild(wrap);
902
903 if (actions.childElementCount === 1) {
904 actions.querySelector('#hf-form-actions-empty').style.display = '';
905 }
906 };
907 }
908
909 function createToggleActionHandler(wrap, content) {
910 return function () {
911 var show = content.offsetParent === null;
912 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
913 content.style.display = show ? 'block' : 'none';
914 };
915 }
916
917 var _default = {
918 init: init
919 };
920 exports["default"] = _default;
921
922 },{}],9:[function(require,module,exports){
923 "use strict";
924
925 Object.defineProperty(exports, "__esModule", {
926 value: true
927 });
928 exports["default"] = void 0;
929
930 // load CodeMirror & plugins
931 var CodeMirror = require('codemirror');
932
933 require('codemirror/mode/xml/xml');
934
935 require('codemirror/mode/javascript/javascript');
936
937 require('codemirror/mode/css/css');
938
939 require('codemirror/mode/htmlmixed/htmlmixed');
940
941 require('codemirror/addon/fold/xml-fold');
942
943 require('codemirror/addon/edit/matchtags');
944
945 require('codemirror/addon/edit/closetag.js');
946
947 var editor, element, dom, requiredFieldsInput, emailFieldsInput, previewFrame, previewDom;
948 var templateRegex = /\{\{ *(\w+)(?:\.([\w.]+))? *(?:\|\| *(\w+))? *\}\}/g;
949
950 function init() {
951 previewFrame = document.getElementById('hf-form-preview');
952 element = document.getElementById('hf-form-editor');
953 dom = document.createElement('form');
954 requiredFieldsInput = document.getElementById('hf-required-fields');
955 emailFieldsInput = document.getElementById('hf-email-fields');
956 dom.innerHTML = element.value;
957 editor = CodeMirror.fromTextArea(element, {
958 selectionPointer: true,
959 matchTags: {
960 bothTags: true
961 },
962 mode: 'htmlmixed',
963 htmlMode: true,
964 autoCloseTags: true,
965 autoRefresh: true,
966 styleActiveLine: true,
967 matchBrackets: true
968 });
969 editor.on('changes', debounce(updatePreview, 500));
970 editor.on('changes', debounce(updateShadowDOM, 100));
971 editor.on('changes', debounce(updateFieldVariables, 500));
972 editor.on('blur', updatePreview);
973 editor.on('blur', updateShadowDOM);
974 editor.on('blur', updateFieldVariables);
975 editor.on('blur', updateRequiredFields);
976 editor.on('blur', updateEmailFields);
977 previewFrame.addEventListener('load', setPreviewDom);
978 setPreviewDom();
979 updateFieldVariables();
980 }
981
982 function setPreviewDom() {
983 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
984 previewDom = frameContent.querySelector('.hf-fields-wrap');
985
986 if (previewDom) {
987 updatePreview();
988 }
989 }
990
991 function getFieldVariableName(f) {
992 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
993 }
994
995 function updateFieldVariables() {
996 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
997 var fieldVariables = uniq([].map.call(fields, function (f) {
998 return '[' + getFieldVariableName(f) + ']';
999 }));
1000 var wpbody = document.getElementById('wpbody-content');
1001 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
1002 // remove existing variables
1003 while (el.firstChild) {
1004 el.removeChild(el.firstChild);
1005 }
1006
1007 var variableElements = fieldVariables.map(function (n) {
1008 // measure width of actual font size for prettiness
1009 var sizeEl = document.createElement('span');
1010 sizeEl.style.visibility = 'hidden';
1011 sizeEl.innerText = n;
1012 wpbody.appendChild(sizeEl);
1013 var width = sizeEl.offsetWidth;
1014 wpbody.removeChild(sizeEl); // add input el
1015
1016 var el = document.createElement('input');
1017 el.type = 'text';
1018 el.style.maxWidth = width * 1.1 + 14 + 'px';
1019 el.value = n;
1020 el.readonly = true;
1021
1022 el.onfocus = function () {
1023 return el.select();
1024 };
1025
1026 return el;
1027 });
1028 variableElements.forEach(function (vel, i, arr) {
1029 el.appendChild(vel);
1030 });
1031 });
1032 }
1033
1034 function updatePreview() {
1035 var markup = editor.getValue(); // replace template tags
1036
1037 markup = markup.replace(templateRegex, function (s, m) {
1038 // if a default value was provided, use that
1039 if (arguments[3]) {
1040 return arguments[3];
1041 }
1042
1043 return '';
1044 }); // update dom
1045
1046 previewDom.innerHTML = markup;
1047 previewDom.dispatchEvent(new Event('hf-refresh'));
1048 }
1049
1050 function updateShadowDOM() {
1051 dom.innerHTML = editor.getValue();
1052 }
1053
1054 function updateRequiredFields() {
1055 var fields = dom.querySelectorAll('[required]');
1056 var fieldNames = [].map.call(fields, getFieldVariableName);
1057 requiredFieldsInput.value = fieldNames.join(',');
1058 }
1059
1060 function updateEmailFields() {
1061 var fields = dom.querySelectorAll('input[type="email"]');
1062 var fieldNames = [].map.call(fields, getFieldVariableName);
1063 emailFieldsInput.value = fieldNames.join(',');
1064 }
1065
1066 function replaceSelection(str) {
1067 editor.replaceSelection(str);
1068 editor.focus();
1069 }
1070
1071 function debounce(func, wait, immediate) {
1072 var timeout;
1073 return function () {
1074 var context = this;
1075 var args = arguments;
1076
1077 var later = function later() {
1078 timeout = null;
1079 if (!immediate) func.apply(context, args);
1080 };
1081
1082 var callNow = immediate && !timeout;
1083 clearTimeout(timeout);
1084 timeout = setTimeout(later, wait);
1085 if (callNow) func.apply(context, args);
1086 };
1087 }
1088
1089 ;
1090
1091 function uniq(a) {
1092 var seen = {};
1093 return a.filter(function (item) {
1094 return seen.hasOwnProperty(item) ? false : seen[item] = true;
1095 });
1096 }
1097
1098 var _default = {
1099 init: init,
1100 replaceSelection: replaceSelection,
1101 updateFieldVariables: updateFieldVariables
1102 };
1103 exports["default"] = _default;
1104
1105 },{"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){
1106 "use strict";
1107
1108 Object.defineProperty(exports, "__esModule", {
1109 value: true
1110 });
1111 exports["default"] = void 0;
1112 var tabs, tabNavs;
1113
1114 function init() {
1115 tabs = document.querySelectorAll('.hf-tab');
1116 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1117
1118 for (var i = 0; i < tabNavs.length; i++) {
1119 tabNavs[i].addEventListener('click', open);
1120 }
1121 }
1122
1123 function open(e) {
1124 var tabTarget = this.getAttribute('data-tab-target');
1125
1126 for (var i = 0; i < tabNavs.length; i++) {
1127 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1128 }
1129
1130 this.blur();
1131
1132 for (var _i = 0; _i < tabs.length; _i++) {
1133 var tab = tabs[_i];
1134 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1135 }
1136
1137 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + ' ');
1138
1139 if (window.history) {
1140 var newUrl = window.location.href;
1141 newUrl = newUrl.replace(/&tab=\w+/, '');
1142 newUrl += '&tab=' + tabTarget;
1143 window.history.replaceState({
1144 tab: tabTarget
1145 }, document.title, newUrl);
1146 }
1147
1148 e.preventDefault();
1149 }
1150
1151 var _default = {
1152 init: init,
1153 open: open
1154 };
1155 exports["default"] = _default;
1156
1157 },{}],11:[function(require,module,exports){
1158 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1159 // Distributed under an MIT license: https://codemirror.net/LICENSE
1160
1161 /**
1162 * Tag-closer extension for CodeMirror.
1163 *
1164 * This extension adds an "autoCloseTags" option that can be set to
1165 * either true to get the default behavior, or an object to further
1166 * configure its behavior.
1167 *
1168 * These are supported options:
1169 *
1170 * `whenClosing` (default true)
1171 * Whether to autoclose when the '/' of a closing tag is typed.
1172 * `whenOpening` (default true)
1173 * Whether to autoclose the tag when the final '>' of an opening
1174 * tag is typed.
1175 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1176 * An array of tag names that should not be autoclosed.
1177 * `indentTags` (default is block tags for HTML, none for XML)
1178 * An array of tag names that should, when opened, cause a
1179 * blank line to be added inside the tag, and the blank line and
1180 * closing line to be indented.
1181 * `emptyTags` (default is none)
1182 * An array of XML tag names that should be autoclosed with '/>'.
1183 *
1184 * See demos/closetag.html for a usage example.
1185 */
1186
1187 (function(mod) {
1188 if (typeof exports == "object" && typeof module == "object") // CommonJS
1189 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1190 else if (typeof define == "function" && define.amd) // AMD
1191 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1192 else // Plain browser env
1193 mod(CodeMirror);
1194 })(function(CodeMirror) {
1195 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1196 if (old != CodeMirror.Init && old)
1197 cm.removeKeyMap("autoCloseTags");
1198 if (!val) return;
1199 var map = {name: "autoCloseTags"};
1200 if (typeof val != "object" || val.whenClosing)
1201 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1202 if (typeof val != "object" || val.whenOpening)
1203 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1204 cm.addKeyMap(map);
1205 });
1206
1207 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1208 "source", "track", "wbr"];
1209 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1210 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1211
1212 function autoCloseGT(cm) {
1213 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1214 var ranges = cm.listSelections(), replacements = [];
1215 var opt = cm.getOption("autoCloseTags");
1216 for (var i = 0; i < ranges.length; i++) {
1217 if (!ranges[i].empty()) return CodeMirror.Pass;
1218 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1219 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1220 var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
1221 var tagName = tagInfo && tagInfo.name
1222 if (!tagName) return CodeMirror.Pass
1223
1224 var html = inner.mode.configuration == "html";
1225 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1226 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1227
1228 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1229 var lowerTagName = tagName.toLowerCase();
1230 // Don't process the '>' at the end of an end-tag or self-closing tag
1231 if (!tagName ||
1232 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1233 tok.type == "tag" && tagInfo.close ||
1234 tok.string.indexOf("/") == (pos.ch - tok.start - 1) || // match something like <someTagName />
1235 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1236 closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))
1237 return CodeMirror.Pass;
1238
1239 var emptyTags = typeof opt == "object" && opt.emptyTags;
1240 if (emptyTags && indexOf(emptyTags, tagName) > -1) {
1241 replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
1242 continue;
1243 }
1244
1245 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1246 replacements[i] = {indent: indent,
1247 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1248 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1249 }
1250
1251 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1252 for (var i = ranges.length - 1; i >= 0; i--) {
1253 var info = replacements[i];
1254 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1255 var sel = cm.listSelections().slice(0);
1256 sel[i] = {head: info.newPos, anchor: info.newPos};
1257 cm.setSelections(sel);
1258 if (!dontIndentOnAutoClose && info.indent) {
1259 cm.indentLine(info.newPos.line, null, true);
1260 cm.indentLine(info.newPos.line + 1, null, true);
1261 }
1262 }
1263 }
1264
1265 function autoCloseCurrent(cm, typingSlash) {
1266 var ranges = cm.listSelections(), replacements = [];
1267 var head = typingSlash ? "/" : "</";
1268 var opt = cm.getOption("autoCloseTags");
1269 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1270 for (var i = 0; i < ranges.length; i++) {
1271 if (!ranges[i].empty()) return CodeMirror.Pass;
1272 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1273 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1274 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1275 tok.start != pos.ch - 1))
1276 return CodeMirror.Pass;
1277 // Kludge to get around the fact that we are not in XML mode
1278 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1279 // work for other XML embedded languages (there is no general
1280 // way to go from a mixed mode to its current XML state).
1281 var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed"
1282 if (mixed && inner.mode.name == "javascript") {
1283 replacement = head + "script";
1284 } else if (mixed && inner.mode.name == "css") {
1285 replacement = head + "style";
1286 } else {
1287 var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
1288 if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos)))
1289 return CodeMirror.Pass;
1290 replacement = head + context[context.length - 1]
1291 }
1292 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1293 replacements[i] = replacement;
1294 }
1295 cm.replaceSelections(replacements);
1296 ranges = cm.listSelections();
1297 if (!dontIndentOnAutoClose) {
1298 for (var i = 0; i < ranges.length; i++)
1299 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1300 cm.indentLine(ranges[i].head.line);
1301 }
1302 }
1303
1304 function autoCloseSlash(cm) {
1305 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1306 return autoCloseCurrent(cm, true);
1307 }
1308
1309 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1310
1311 function indexOf(collection, elt) {
1312 if (collection.indexOf) return collection.indexOf(elt);
1313 for (var i = 0, e = collection.length; i < e; ++i)
1314 if (collection[i] == elt) return i;
1315 return -1;
1316 }
1317
1318 // If xml-fold is loaded, we use its functionality to try and verify
1319 // whether a given tag is actually unclosed.
1320 function closingTagExists(cm, context, tagName, pos, newTag) {
1321 if (!CodeMirror.scanForClosingTag) return false;
1322 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1323 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1324 if (!nextClose || nextClose.tag != tagName) return false;
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 var onCx = newTag ? 1 : 0
1329 for (var i = context.length - 1; i >= 0; i--) {
1330 if (context[i] == tagName) ++onCx
1331 else break
1332 }
1333 pos = nextClose.to;
1334 for (var i = 1; i < onCx; i++) {
1335 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1336 if (!next || next.tag != tagName) return false;
1337 pos = next.to;
1338 }
1339 return true;
1340 }
1341 });
1342
1343 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],12:[function(require,module,exports){
1344 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1345 // Distributed under an MIT license: https://codemirror.net/LICENSE
1346
1347 (function(mod) {
1348 if (typeof exports == "object" && typeof module == "object") // CommonJS
1349 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1350 else if (typeof define == "function" && define.amd) // AMD
1351 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1352 else // Plain browser env
1353 mod(CodeMirror);
1354 })(function(CodeMirror) {
1355 "use strict";
1356
1357 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1358 if (old && old != CodeMirror.Init) {
1359 cm.off("cursorActivity", doMatchTags);
1360 cm.off("viewportChange", maybeUpdateMatch);
1361 clear(cm);
1362 }
1363 if (val) {
1364 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1365 cm.on("cursorActivity", doMatchTags);
1366 cm.on("viewportChange", maybeUpdateMatch);
1367 doMatchTags(cm);
1368 }
1369 });
1370
1371 function clear(cm) {
1372 if (cm.state.tagHit) cm.state.tagHit.clear();
1373 if (cm.state.tagOther) cm.state.tagOther.clear();
1374 cm.state.tagHit = cm.state.tagOther = null;
1375 }
1376
1377 function doMatchTags(cm) {
1378 cm.state.failedTagMatch = false;
1379 cm.operation(function() {
1380 clear(cm);
1381 if (cm.somethingSelected()) return;
1382 var cur = cm.getCursor(), range = cm.getViewport();
1383 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1384 var match = CodeMirror.findMatchingTag(cm, cur, range);
1385 if (!match) return;
1386 if (cm.state.matchBothTags) {
1387 var hit = match.at == "open" ? match.open : match.close;
1388 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1389 }
1390 var other = match.at == "close" ? match.open : match.close;
1391 if (other)
1392 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1393 else
1394 cm.state.failedTagMatch = true;
1395 });
1396 }
1397
1398 function maybeUpdateMatch(cm) {
1399 if (cm.state.failedTagMatch) doMatchTags(cm);
1400 }
1401
1402 CodeMirror.commands.toMatchingTag = function(cm) {
1403 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1404 if (found) {
1405 var other = found.at == "close" ? found.open : found.close;
1406 if (other) cm.extendSelection(other.to, other.from);
1407 }
1408 };
1409 });
1410
1411 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],13:[function(require,module,exports){
1412 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1413 // Distributed under an MIT license: https://codemirror.net/LICENSE
1414
1415 (function(mod) {
1416 if (typeof exports == "object" && typeof module == "object") // CommonJS
1417 mod(require("../../lib/codemirror"));
1418 else if (typeof define == "function" && define.amd) // AMD
1419 define(["../../lib/codemirror"], mod);
1420 else // Plain browser env
1421 mod(CodeMirror);
1422 })(function(CodeMirror) {
1423 "use strict";
1424
1425 var Pos = CodeMirror.Pos;
1426 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1427
1428 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";
1429 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1430 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1431
1432 function Iter(cm, line, ch, range) {
1433 this.line = line; this.ch = ch;
1434 this.cm = cm; this.text = cm.getLine(line);
1435 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1436 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1437 }
1438
1439 function tagAt(iter, ch) {
1440 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1441 return type && /\btag\b/.test(type);
1442 }
1443
1444 function nextLine(iter) {
1445 if (iter.line >= iter.max) return;
1446 iter.ch = 0;
1447 iter.text = iter.cm.getLine(++iter.line);
1448 return true;
1449 }
1450 function prevLine(iter) {
1451 if (iter.line <= iter.min) return;
1452 iter.text = iter.cm.getLine(--iter.line);
1453 iter.ch = iter.text.length;
1454 return true;
1455 }
1456
1457 function toTagEnd(iter) {
1458 for (;;) {
1459 var gt = iter.text.indexOf(">", iter.ch);
1460 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1461 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1462 var lastSlash = iter.text.lastIndexOf("/", gt);
1463 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1464 iter.ch = gt + 1;
1465 return selfClose ? "selfClose" : "regular";
1466 }
1467 }
1468 function toTagStart(iter) {
1469 for (;;) {
1470 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1471 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1472 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1473 xmlTagStart.lastIndex = lt;
1474 iter.ch = lt;
1475 var match = xmlTagStart.exec(iter.text);
1476 if (match && match.index == lt) return match;
1477 }
1478 }
1479
1480 function toNextTag(iter) {
1481 for (;;) {
1482 xmlTagStart.lastIndex = iter.ch;
1483 var found = xmlTagStart.exec(iter.text);
1484 if (!found) { if (nextLine(iter)) continue; else return; }
1485 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1486 iter.ch = found.index + found[0].length;
1487 return found;
1488 }
1489 }
1490 function toPrevTag(iter) {
1491 for (;;) {
1492 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1493 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1494 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1495 var lastSlash = iter.text.lastIndexOf("/", gt);
1496 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1497 iter.ch = gt + 1;
1498 return selfClose ? "selfClose" : "regular";
1499 }
1500 }
1501
1502 function findMatchingClose(iter, tag) {
1503 var stack = [];
1504 for (;;) {
1505 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1506 if (!next || !(end = toTagEnd(iter))) return;
1507 if (end == "selfClose") continue;
1508 if (next[1]) { // closing tag
1509 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1510 stack.length = i;
1511 break;
1512 }
1513 if (i < 0 && (!tag || tag == next[2])) return {
1514 tag: next[2],
1515 from: Pos(startLine, startCh),
1516 to: Pos(iter.line, iter.ch)
1517 };
1518 } else { // opening tag
1519 stack.push(next[2]);
1520 }
1521 }
1522 }
1523 function findMatchingOpen(iter, tag) {
1524 var stack = [];
1525 for (;;) {
1526 var prev = toPrevTag(iter);
1527 if (!prev) return;
1528 if (prev == "selfClose") { toTagStart(iter); continue; }
1529 var endLine = iter.line, endCh = iter.ch;
1530 var start = toTagStart(iter);
1531 if (!start) return;
1532 if (start[1]) { // closing tag
1533 stack.push(start[2]);
1534 } else { // opening tag
1535 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1536 stack.length = i;
1537 break;
1538 }
1539 if (i < 0 && (!tag || tag == start[2])) return {
1540 tag: start[2],
1541 from: Pos(iter.line, iter.ch),
1542 to: Pos(endLine, endCh)
1543 };
1544 }
1545 }
1546 }
1547
1548 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1549 var iter = new Iter(cm, start.line, 0);
1550 for (;;) {
1551 var openTag = toNextTag(iter)
1552 if (!openTag || iter.line != start.line) return
1553 var end = toTagEnd(iter)
1554 if (!end) return
1555 if (!openTag[1] && end != "selfClose") {
1556 var startPos = Pos(iter.line, iter.ch);
1557 var endPos = findMatchingClose(iter, openTag[2]);
1558 return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
1559 }
1560 }
1561 });
1562 CodeMirror.findMatchingTag = function(cm, pos, range) {
1563 var iter = new Iter(cm, pos.line, pos.ch, range);
1564 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1565 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1566 var start = end && toTagStart(iter);
1567 if (!end || !start || cmp(iter, pos) > 0) return;
1568 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1569 if (end == "selfClose") return {open: here, close: null, at: "open"};
1570
1571 if (start[1]) { // closing tag
1572 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1573 } else { // opening tag
1574 iter = new Iter(cm, to.line, to.ch, range);
1575 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1576 }
1577 };
1578
1579 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1580 var iter = new Iter(cm, pos.line, pos.ch, range);
1581 for (;;) {
1582 var open = findMatchingOpen(iter, tag);
1583 if (!open) break;
1584 var forward = new Iter(cm, pos.line, pos.ch, range);
1585 var close = findMatchingClose(forward, open.tag);
1586 if (close) return {open: open, close: close};
1587 }
1588 };
1589
1590 // Used by addon/edit/closetag.js
1591 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1592 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1593 return findMatchingClose(iter, name);
1594 };
1595 });
1596
1597 },{"../../lib/codemirror":14}],14:[function(require,module,exports){
1598 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1599 // Distributed under an MIT license: https://codemirror.net/LICENSE
1600
1601 // This is CodeMirror (https://codemirror.net), a code editor
1602 // implemented in JavaScript on top of the browser's DOM.
1603 //
1604 // You can find some technical background for some of the code below
1605 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1606
1607 (function (global, factory) {
1608 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1609 typeof define === 'function' && define.amd ? define(factory) :
1610 (global = global || self, global.CodeMirror = factory());
1611 }(this, (function () { 'use strict';
1612
1613 // Kludges for bugs and behavior differences that can't be feature
1614 // detected are enabled based on userAgent etc sniffing.
1615 var userAgent = navigator.userAgent;
1616 var platform = navigator.platform;
1617
1618 var gecko = /gecko\/\d/i.test(userAgent);
1619 var ie_upto10 = /MSIE \d/.test(userAgent);
1620 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1621 var edge = /Edge\/(\d+)/.exec(userAgent);
1622 var ie = ie_upto10 || ie_11up || edge;
1623 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1624 var webkit = !edge && /WebKit\//.test(userAgent);
1625 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1626 var chrome = !edge && /Chrome\//.test(userAgent);
1627 var presto = /Opera\//.test(userAgent);
1628 var safari = /Apple Computer/.test(navigator.vendor);
1629 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1630 var phantom = /PhantomJS/.test(userAgent);
1631
1632 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1633 var android = /Android/.test(userAgent);
1634 // This is woefully incomplete. Suggestions for alternative methods welcome.
1635 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1636 var mac = ios || /Mac/.test(platform);
1637 var chromeOS = /\bCrOS\b/.test(userAgent);
1638 var windows = /win/i.test(platform);
1639
1640 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1641 if (presto_version) { presto_version = Number(presto_version[1]); }
1642 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1643 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1644 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1645 var captureRightClick = gecko || (ie && ie_version >= 9);
1646
1647 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1648
1649 var rmClass = function(node, cls) {
1650 var current = node.className;
1651 var match = classTest(cls).exec(current);
1652 if (match) {
1653 var after = current.slice(match.index + match[0].length);
1654 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1655 }
1656 };
1657
1658 function removeChildren(e) {
1659 for (var count = e.childNodes.length; count > 0; --count)
1660 { e.removeChild(e.firstChild); }
1661 return e
1662 }
1663
1664 function removeChildrenAndAdd(parent, e) {
1665 return removeChildren(parent).appendChild(e)
1666 }
1667
1668 function elt(tag, content, className, style) {
1669 var e = document.createElement(tag);
1670 if (className) { e.className = className; }
1671 if (style) { e.style.cssText = style; }
1672 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1673 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1674 return e
1675 }
1676 // wrapper for elt, which removes the elt from the accessibility tree
1677 function eltP(tag, content, className, style) {
1678 var e = elt(tag, content, className, style);
1679 e.setAttribute("role", "presentation");
1680 return e
1681 }
1682
1683 var range;
1684 if (document.createRange) { range = function(node, start, end, endNode) {
1685 var r = document.createRange();
1686 r.setEnd(endNode || node, end);
1687 r.setStart(node, start);
1688 return r
1689 }; }
1690 else { range = function(node, start, end) {
1691 var r = document.body.createTextRange();
1692 try { r.moveToElementText(node.parentNode); }
1693 catch(e) { return r }
1694 r.collapse(true);
1695 r.moveEnd("character", end);
1696 r.moveStart("character", start);
1697 return r
1698 }; }
1699
1700 function contains(parent, child) {
1701 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1702 { child = child.parentNode; }
1703 if (parent.contains)
1704 { return parent.contains(child) }
1705 do {
1706 if (child.nodeType == 11) { child = child.host; }
1707 if (child == parent) { return true }
1708 } while (child = child.parentNode)
1709 }
1710
1711 function activeElt() {
1712 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1713 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1714 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1715 var activeElement;
1716 try {
1717 activeElement = document.activeElement;
1718 } catch(e) {
1719 activeElement = document.body || null;
1720 }
1721 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1722 { activeElement = activeElement.shadowRoot.activeElement; }
1723 return activeElement
1724 }
1725
1726 function addClass(node, cls) {
1727 var current = node.className;
1728 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1729 }
1730 function joinClasses(a, b) {
1731 var as = a.split(" ");
1732 for (var i = 0; i < as.length; i++)
1733 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1734 return b
1735 }
1736
1737 var selectInput = function(node) { node.select(); };
1738 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1739 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1740 else if (ie) // Suppress mysterious IE10 errors
1741 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1742
1743 function bind(f) {
1744 var args = Array.prototype.slice.call(arguments, 1);
1745 return function(){return f.apply(null, args)}
1746 }
1747
1748 function copyObj(obj, target, overwrite) {
1749 if (!target) { target = {}; }
1750 for (var prop in obj)
1751 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1752 { target[prop] = obj[prop]; } }
1753 return target
1754 }
1755
1756 // Counts the column offset in a string, taking tabs into account.
1757 // Used mostly to find indentation.
1758 function countColumn(string, end, tabSize, startIndex, startValue) {
1759 if (end == null) {
1760 end = string.search(/[^\s\u00a0]/);
1761 if (end == -1) { end = string.length; }
1762 }
1763 for (var i = startIndex || 0, n = startValue || 0;;) {
1764 var nextTab = string.indexOf("\t", i);
1765 if (nextTab < 0 || nextTab >= end)
1766 { return n + (end - i) }
1767 n += nextTab - i;
1768 n += tabSize - (n % tabSize);
1769 i = nextTab + 1;
1770 }
1771 }
1772
1773 var Delayed = function() {
1774 this.id = null;
1775 this.f = null;
1776 this.time = 0;
1777 this.handler = bind(this.onTimeout, this);
1778 };
1779 Delayed.prototype.onTimeout = function (self) {
1780 self.id = 0;
1781 if (self.time <= +new Date) {
1782 self.f();
1783 } else {
1784 setTimeout(self.handler, self.time - +new Date);
1785 }
1786 };
1787 Delayed.prototype.set = function (ms, f) {
1788 this.f = f;
1789 var time = +new Date + ms;
1790 if (!this.id || time < this.time) {
1791 clearTimeout(this.id);
1792 this.id = setTimeout(this.handler, ms);
1793 this.time = time;
1794 }
1795 };
1796
1797 function indexOf(array, elt) {
1798 for (var i = 0; i < array.length; ++i)
1799 { if (array[i] == elt) { return i } }
1800 return -1
1801 }
1802
1803 // Number of pixels added to scroller and sizer to hide scrollbar
1804 var scrollerGap = 30;
1805
1806 // Returned or thrown by various protocols to signal 'I'm not
1807 // handling this'.
1808 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1809
1810 // Reused option objects for setSelection & friends
1811 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
1812
1813 // The inverse of countColumn -- find the offset that corresponds to
1814 // a particular column.
1815 function findColumn(string, goal, tabSize) {
1816 for (var pos = 0, col = 0;;) {
1817 var nextTab = string.indexOf("\t", pos);
1818 if (nextTab == -1) { nextTab = string.length; }
1819 var skipped = nextTab - pos;
1820 if (nextTab == string.length || col + skipped >= goal)
1821 { return pos + Math.min(skipped, goal - col) }
1822 col += nextTab - pos;
1823 col += tabSize - (col % tabSize);
1824 pos = nextTab + 1;
1825 if (col >= goal) { return pos }
1826 }
1827 }
1828
1829 var spaceStrs = [""];
1830 function spaceStr(n) {
1831 while (spaceStrs.length <= n)
1832 { spaceStrs.push(lst(spaceStrs) + " "); }
1833 return spaceStrs[n]
1834 }
1835
1836 function lst(arr) { return arr[arr.length-1] }
1837
1838 function map(array, f) {
1839 var out = [];
1840 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1841 return out
1842 }
1843
1844 function insertSorted(array, value, score) {
1845 var pos = 0, priority = score(value);
1846 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1847 array.splice(pos, 0, value);
1848 }
1849
1850 function nothing() {}
1851
1852 function createObj(base, props) {
1853 var inst;
1854 if (Object.create) {
1855 inst = Object.create(base);
1856 } else {
1857 nothing.prototype = base;
1858 inst = new nothing();
1859 }
1860 if (props) { copyObj(props, inst); }
1861 return inst
1862 }
1863
1864 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1865 function isWordCharBasic(ch) {
1866 return /\w/.test(ch) || ch > "\x80" &&
1867 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1868 }
1869 function isWordChar(ch, helper) {
1870 if (!helper) { return isWordCharBasic(ch) }
1871 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1872 return helper.test(ch)
1873 }
1874
1875 function isEmpty(obj) {
1876 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1877 return true
1878 }
1879
1880 // Extending unicode characters. A series of a non-extending char +
1881 // any number of extending chars is treated as a single unit as far
1882 // as editing and measuring is concerned. This is not fully correct,
1883 // since some scripts/fonts/browsers also treat other configurations
1884 // of code points as a group.
1885 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]/;
1886 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1887
1888 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1889 function skipExtendingChars(str, pos, dir) {
1890 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1891 return pos
1892 }
1893
1894 // Returns the value from the range [`from`; `to`] that satisfies
1895 // `pred` and is closest to `from`. Assumes that at least `to`
1896 // satisfies `pred`. Supports `from` being greater than `to`.
1897 function findFirst(pred, from, to) {
1898 // At any point we are certain `to` satisfies `pred`, don't know
1899 // whether `from` does.
1900 var dir = from > to ? -1 : 1;
1901 for (;;) {
1902 if (from == to) { return from }
1903 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1904 if (mid == from) { return pred(mid) ? from : to }
1905 if (pred(mid)) { to = mid; }
1906 else { from = mid + dir; }
1907 }
1908 }
1909
1910 // BIDI HELPERS
1911
1912 function iterateBidiSections(order, from, to, f) {
1913 if (!order) { return f(from, to, "ltr", 0) }
1914 var found = false;
1915 for (var i = 0; i < order.length; ++i) {
1916 var part = order[i];
1917 if (part.from < to && part.to > from || from == to && part.to == from) {
1918 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
1919 found = true;
1920 }
1921 }
1922 if (!found) { f(from, to, "ltr"); }
1923 }
1924
1925 var bidiOther = null;
1926 function getBidiPartAt(order, ch, sticky) {
1927 var found;
1928 bidiOther = null;
1929 for (var i = 0; i < order.length; ++i) {
1930 var cur = order[i];
1931 if (cur.from < ch && cur.to > ch) { return i }
1932 if (cur.to == ch) {
1933 if (cur.from != cur.to && sticky == "before") { found = i; }
1934 else { bidiOther = i; }
1935 }
1936 if (cur.from == ch) {
1937 if (cur.from != cur.to && sticky != "before") { found = i; }
1938 else { bidiOther = i; }
1939 }
1940 }
1941 return found != null ? found : bidiOther
1942 }
1943
1944 // Bidirectional ordering algorithm
1945 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
1946 // that this (partially) implements.
1947
1948 // One-char codes used for character types:
1949 // L (L): Left-to-Right
1950 // R (R): Right-to-Left
1951 // r (AL): Right-to-Left Arabic
1952 // 1 (EN): European Number
1953 // + (ES): European Number Separator
1954 // % (ET): European Number Terminator
1955 // n (AN): Arabic Number
1956 // , (CS): Common Number Separator
1957 // m (NSM): Non-Spacing Mark
1958 // b (BN): Boundary Neutral
1959 // s (B): Paragraph Separator
1960 // t (S): Segment Separator
1961 // w (WS): Whitespace
1962 // N (ON): Other Neutrals
1963
1964 // Returns null if characters are ordered as they appear
1965 // (left-to-right), or an array of sections ({from, to, level}
1966 // objects) in the order in which they occur visually.
1967 var bidiOrdering = (function() {
1968 // Character types for codepoints 0 to 0xff
1969 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
1970 // Character types for codepoints 0x600 to 0x6f9
1971 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
1972 function charType(code) {
1973 if (code <= 0xf7) { return lowTypes.charAt(code) }
1974 else if (0x590 <= code && code <= 0x5f4) { return "R" }
1975 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
1976 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
1977 else if (0x2000 <= code && code <= 0x200b) { return "w" }
1978 else if (code == 0x200c) { return "b" }
1979 else { return "L" }
1980 }
1981
1982 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
1983 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
1984
1985 function BidiSpan(level, from, to) {
1986 this.level = level;
1987 this.from = from; this.to = to;
1988 }
1989
1990 return function(str, direction) {
1991 var outerType = direction == "ltr" ? "L" : "R";
1992
1993 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
1994 var len = str.length, types = [];
1995 for (var i = 0; i < len; ++i)
1996 { types.push(charType(str.charCodeAt(i))); }
1997
1998 // W1. Examine each non-spacing mark (NSM) in the level run, and
1999 // change the type of the NSM to the type of the previous
2000 // character. If the NSM is at the start of the level run, it will
2001 // get the type of sor.
2002 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2003 var type = types[i$1];
2004 if (type == "m") { types[i$1] = prev; }
2005 else { prev = type; }
2006 }
2007
2008 // W2. Search backwards from each instance of a European number
2009 // until the first strong type (R, L, AL, or sor) is found. If an
2010 // AL is found, change the type of the European number to Arabic
2011 // number.
2012 // W3. Change all ALs to R.
2013 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2014 var type$1 = types[i$2];
2015 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2016 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2017 }
2018
2019 // W4. A single European separator between two European numbers
2020 // changes to a European number. A single common separator between
2021 // two numbers of the same type changes to that type.
2022 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2023 var type$2 = types[i$3];
2024 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2025 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2026 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2027 prev$1 = type$2;
2028 }
2029
2030 // W5. A sequence of European terminators adjacent to European
2031 // numbers changes to all European numbers.
2032 // W6. Otherwise, separators and terminators change to Other
2033 // Neutral.
2034 for (var i$4 = 0; i$4 < len; ++i$4) {
2035 var type$3 = types[i$4];
2036 if (type$3 == ",") { types[i$4] = "N"; }
2037 else if (type$3 == "%") {
2038 var end = (void 0);
2039 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2040 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2041 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2042 i$4 = end - 1;
2043 }
2044 }
2045
2046 // W7. Search backwards from each instance of a European number
2047 // until the first strong type (R, L, or sor) is found. If an L is
2048 // found, then change the type of the European number to L.
2049 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2050 var type$4 = types[i$5];
2051 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2052 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2053 }
2054
2055 // N1. A sequence of neutrals takes the direction of the
2056 // surrounding strong text if the text on both sides has the same
2057 // direction. European and Arabic numbers act as if they were R in
2058 // terms of their influence on neutrals. Start-of-level-run (sor)
2059 // and end-of-level-run (eor) are used at level run boundaries.
2060 // N2. Any remaining neutrals take the embedding direction.
2061 for (var i$6 = 0; i$6 < len; ++i$6) {
2062 if (isNeutral.test(types[i$6])) {
2063 var end$1 = (void 0);
2064 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2065 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2066 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2067 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2068 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2069 i$6 = end$1 - 1;
2070 }
2071 }
2072
2073 // Here we depart from the documented algorithm, in order to avoid
2074 // building up an actual levels array. Since there are only three
2075 // levels (0, 1, 2) in an implementation that doesn't take
2076 // explicit embedding into account, we can build up the order on
2077 // the fly, without following the level-based algorithm.
2078 var order = [], m;
2079 for (var i$7 = 0; i$7 < len;) {
2080 if (countsAsLeft.test(types[i$7])) {
2081 var start = i$7;
2082 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2083 order.push(new BidiSpan(0, start, i$7));
2084 } else {
2085 var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
2086 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2087 for (var j$2 = pos; j$2 < i$7;) {
2088 if (countsAsNum.test(types[j$2])) {
2089 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
2090 var nstart = j$2;
2091 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2092 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2093 at += isRTL;
2094 pos = j$2;
2095 } else { ++j$2; }
2096 }
2097 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2098 }
2099 }
2100 if (direction == "ltr") {
2101 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2102 order[0].from = m[0].length;
2103 order.unshift(new BidiSpan(0, 0, m[0].length));
2104 }
2105 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2106 lst(order).to -= m[0].length;
2107 order.push(new BidiSpan(0, len - m[0].length, len));
2108 }
2109 }
2110
2111 return direction == "rtl" ? order.reverse() : order
2112 }
2113 })();
2114
2115 // Get the bidi ordering for the given line (and cache it). Returns
2116 // false for lines that are fully left-to-right, and an array of
2117 // BidiSpan objects otherwise.
2118 function getOrder(line, direction) {
2119 var order = line.order;
2120 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2121 return order
2122 }
2123
2124 // EVENT HANDLING
2125
2126 // Lightweight event framework. on/off also work on DOM nodes,
2127 // registering native DOM handlers.
2128
2129 var noHandlers = [];
2130
2131 var on = function(emitter, type, f) {
2132 if (emitter.addEventListener) {
2133 emitter.addEventListener(type, f, false);
2134 } else if (emitter.attachEvent) {
2135 emitter.attachEvent("on" + type, f);
2136 } else {
2137 var map = emitter._handlers || (emitter._handlers = {});
2138 map[type] = (map[type] || noHandlers).concat(f);
2139 }
2140 };
2141
2142 function getHandlers(emitter, type) {
2143 return emitter._handlers && emitter._handlers[type] || noHandlers
2144 }
2145
2146 function off(emitter, type, f) {
2147 if (emitter.removeEventListener) {
2148 emitter.removeEventListener(type, f, false);
2149 } else if (emitter.detachEvent) {
2150 emitter.detachEvent("on" + type, f);
2151 } else {
2152 var map = emitter._handlers, arr = map && map[type];
2153 if (arr) {
2154 var index = indexOf(arr, f);
2155 if (index > -1)
2156 { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2157 }
2158 }
2159 }
2160
2161 function signal(emitter, type /*, values...*/) {
2162 var handlers = getHandlers(emitter, type);
2163 if (!handlers.length) { return }
2164 var args = Array.prototype.slice.call(arguments, 2);
2165 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2166 }
2167
2168 // The DOM events that CodeMirror handles can be overridden by
2169 // registering a (non-DOM) handler on the editor for the event name,
2170 // and preventDefault-ing the event in that handler.
2171 function signalDOMEvent(cm, e, override) {
2172 if (typeof e == "string")
2173 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2174 signal(cm, override || e.type, cm, e);
2175 return e_defaultPrevented(e) || e.codemirrorIgnore
2176 }
2177
2178 function signalCursorActivity(cm) {
2179 var arr = cm._handlers && cm._handlers.cursorActivity;
2180 if (!arr) { return }
2181 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2182 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2183 { set.push(arr[i]); } }
2184 }
2185
2186 function hasHandler(emitter, type) {
2187 return getHandlers(emitter, type).length > 0
2188 }
2189
2190 // Add on and off methods to a constructor's prototype, to make
2191 // registering events on such objects more convenient.
2192 function eventMixin(ctor) {
2193 ctor.prototype.on = function(type, f) {on(this, type, f);};
2194 ctor.prototype.off = function(type, f) {off(this, type, f);};
2195 }
2196
2197 // Due to the fact that we still support jurassic IE versions, some
2198 // compatibility wrappers are needed.
2199
2200 function e_preventDefault(e) {
2201 if (e.preventDefault) { e.preventDefault(); }
2202 else { e.returnValue = false; }
2203 }
2204 function e_stopPropagation(e) {
2205 if (e.stopPropagation) { e.stopPropagation(); }
2206 else { e.cancelBubble = true; }
2207 }
2208 function e_defaultPrevented(e) {
2209 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2210 }
2211 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2212
2213 function e_target(e) {return e.target || e.srcElement}
2214 function e_button(e) {
2215 var b = e.which;
2216 if (b == null) {
2217 if (e.button & 1) { b = 1; }
2218 else if (e.button & 2) { b = 3; }
2219 else if (e.button & 4) { b = 2; }
2220 }
2221 if (mac && e.ctrlKey && b == 1) { b = 3; }
2222 return b
2223 }
2224
2225 // Detect drag-and-drop
2226 var dragAndDrop = function() {
2227 // There is *some* kind of drag-and-drop support in IE6-8, but I
2228 // couldn't get it to work yet.
2229 if (ie && ie_version < 9) { return false }
2230 var div = elt('div');
2231 return "draggable" in div || "dragDrop" in div
2232 }();
2233
2234 var zwspSupported;
2235 function zeroWidthElement(measure) {
2236 if (zwspSupported == null) {
2237 var test = elt("span", "\u200b");
2238 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2239 if (measure.firstChild.offsetHeight != 0)
2240 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2241 }
2242 var node = zwspSupported ? elt("span", "\u200b") :
2243 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2244 node.setAttribute("cm-text", "");
2245 return node
2246 }
2247
2248 // Feature-detect IE's crummy client rect reporting for bidi text
2249 var badBidiRects;
2250 function hasBadBidiRects(measure) {
2251 if (badBidiRects != null) { return badBidiRects }
2252 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2253 var r0 = range(txt, 0, 1).getBoundingClientRect();
2254 var r1 = range(txt, 1, 2).getBoundingClientRect();
2255 removeChildren(measure);
2256 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2257 return badBidiRects = (r1.right - r0.right < 3)
2258 }
2259
2260 // See if "".split is the broken IE version, if so, provide an
2261 // alternative way to split lines.
2262 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2263 var pos = 0, result = [], l = string.length;
2264 while (pos <= l) {
2265 var nl = string.indexOf("\n", pos);
2266 if (nl == -1) { nl = string.length; }
2267 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2268 var rt = line.indexOf("\r");
2269 if (rt != -1) {
2270 result.push(line.slice(0, rt));
2271 pos += rt + 1;
2272 } else {
2273 result.push(line);
2274 pos = nl + 1;
2275 }
2276 }
2277 return result
2278 } : function (string) { return string.split(/\r\n?|\n/); };
2279
2280 var hasSelection = window.getSelection ? function (te) {
2281 try { return te.selectionStart != te.selectionEnd }
2282 catch(e) { return false }
2283 } : function (te) {
2284 var range;
2285 try {range = te.ownerDocument.selection.createRange();}
2286 catch(e) {}
2287 if (!range || range.parentElement() != te) { return false }
2288 return range.compareEndPoints("StartToEnd", range) != 0
2289 };
2290
2291 var hasCopyEvent = (function () {
2292 var e = elt("div");
2293 if ("oncopy" in e) { return true }
2294 e.setAttribute("oncopy", "return;");
2295 return typeof e.oncopy == "function"
2296 })();
2297
2298 var badZoomedRects = null;
2299 function hasBadZoomedRects(measure) {
2300 if (badZoomedRects != null) { return badZoomedRects }
2301 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2302 var normal = node.getBoundingClientRect();
2303 var fromRange = range(node, 0, 1).getBoundingClientRect();
2304 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2305 }
2306
2307 // Known modes, by name and by MIME
2308 var modes = {}, mimeModes = {};
2309
2310 // Extra arguments are stored as the mode's dependencies, which is
2311 // used by (legacy) mechanisms like loadmode.js to automatically
2312 // load a mode. (Preferred mechanism is the require/define calls.)
2313 function defineMode(name, mode) {
2314 if (arguments.length > 2)
2315 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2316 modes[name] = mode;
2317 }
2318
2319 function defineMIME(mime, spec) {
2320 mimeModes[mime] = spec;
2321 }
2322
2323 // Given a MIME type, a {name, ...options} config object, or a name
2324 // string, return a mode config object.
2325 function resolveMode(spec) {
2326 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2327 spec = mimeModes[spec];
2328 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2329 var found = mimeModes[spec.name];
2330 if (typeof found == "string") { found = {name: found}; }
2331 spec = createObj(found, spec);
2332 spec.name = found.name;
2333 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2334 return resolveMode("application/xml")
2335 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2336 return resolveMode("application/json")
2337 }
2338 if (typeof spec == "string") { return {name: spec} }
2339 else { return spec || {name: "null"} }
2340 }
2341
2342 // Given a mode spec (anything that resolveMode accepts), find and
2343 // initialize an actual mode object.
2344 function getMode(options, spec) {
2345 spec = resolveMode(spec);
2346 var mfactory = modes[spec.name];
2347 if (!mfactory) { return getMode(options, "text/plain") }
2348 var modeObj = mfactory(options, spec);
2349 if (modeExtensions.hasOwnProperty(spec.name)) {
2350 var exts = modeExtensions[spec.name];
2351 for (var prop in exts) {
2352 if (!exts.hasOwnProperty(prop)) { continue }
2353 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2354 modeObj[prop] = exts[prop];
2355 }
2356 }
2357 modeObj.name = spec.name;
2358 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2359 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2360 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2361
2362 return modeObj
2363 }
2364
2365 // This can be used to attach properties to mode objects from
2366 // outside the actual mode definition.
2367 var modeExtensions = {};
2368 function extendMode(mode, properties) {
2369 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2370 copyObj(properties, exts);
2371 }
2372
2373 function copyState(mode, state) {
2374 if (state === true) { return state }
2375 if (mode.copyState) { return mode.copyState(state) }
2376 var nstate = {};
2377 for (var n in state) {
2378 var val = state[n];
2379 if (val instanceof Array) { val = val.concat([]); }
2380 nstate[n] = val;
2381 }
2382 return nstate
2383 }
2384
2385 // Given a mode and a state (for that mode), find the inner mode and
2386 // state at the position that the state refers to.
2387 function innerMode(mode, state) {
2388 var info;
2389 while (mode.innerMode) {
2390 info = mode.innerMode(state);
2391 if (!info || info.mode == mode) { break }
2392 state = info.state;
2393 mode = info.mode;
2394 }
2395 return info || {mode: mode, state: state}
2396 }
2397
2398 function startState(mode, a1, a2) {
2399 return mode.startState ? mode.startState(a1, a2) : true
2400 }
2401
2402 // STRING STREAM
2403
2404 // Fed to the mode parsers, provides helper functions to make
2405 // parsers more succinct.
2406
2407 var StringStream = function(string, tabSize, lineOracle) {
2408 this.pos = this.start = 0;
2409 this.string = string;
2410 this.tabSize = tabSize || 8;
2411 this.lastColumnPos = this.lastColumnValue = 0;
2412 this.lineStart = 0;
2413 this.lineOracle = lineOracle;
2414 };
2415
2416 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
2417 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
2418 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
2419 StringStream.prototype.next = function () {
2420 if (this.pos < this.string.length)
2421 { return this.string.charAt(this.pos++) }
2422 };
2423 StringStream.prototype.eat = function (match) {
2424 var ch = this.string.charAt(this.pos);
2425 var ok;
2426 if (typeof match == "string") { ok = ch == match; }
2427 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
2428 if (ok) {++this.pos; return ch}
2429 };
2430 StringStream.prototype.eatWhile = function (match) {
2431 var start = this.pos;
2432 while (this.eat(match)){}
2433 return this.pos > start
2434 };
2435 StringStream.prototype.eatSpace = function () {
2436 var start = this.pos;
2437 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
2438 return this.pos > start
2439 };
2440 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
2441 StringStream.prototype.skipTo = function (ch) {
2442 var found = this.string.indexOf(ch, this.pos);
2443 if (found > -1) {this.pos = found; return true}
2444 };
2445 StringStream.prototype.backUp = function (n) {this.pos -= n;};
2446 StringStream.prototype.column = function () {
2447 if (this.lastColumnPos < this.start) {
2448 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
2449 this.lastColumnPos = this.start;
2450 }
2451 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2452 };
2453 StringStream.prototype.indentation = function () {
2454 return countColumn(this.string, null, this.tabSize) -
2455 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2456 };
2457 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
2458 if (typeof pattern == "string") {
2459 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
2460 var substr = this.string.substr(this.pos, pattern.length);
2461 if (cased(substr) == cased(pattern)) {
2462 if (consume !== false) { this.pos += pattern.length; }
2463 return true
2464 }
2465 } else {
2466 var match = this.string.slice(this.pos).match(pattern);
2467 if (match && match.index > 0) { return null }
2468 if (match && consume !== false) { this.pos += match[0].length; }
2469 return match
2470 }
2471 };
2472 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
2473 StringStream.prototype.hideFirstChars = function (n, inner) {
2474 this.lineStart += n;
2475 try { return inner() }
2476 finally { this.lineStart -= n; }
2477 };
2478 StringStream.prototype.lookAhead = function (n) {
2479 var oracle = this.lineOracle;
2480 return oracle && oracle.lookAhead(n)
2481 };
2482 StringStream.prototype.baseToken = function () {
2483 var oracle = this.lineOracle;
2484 return oracle && oracle.baseToken(this.pos)
2485 };
2486
2487 // Find the line object corresponding to the given line number.
2488 function getLine(doc, n) {
2489 n -= doc.first;
2490 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
2491 var chunk = doc;
2492 while (!chunk.lines) {
2493 for (var i = 0;; ++i) {
2494 var child = chunk.children[i], sz = child.chunkSize();
2495 if (n < sz) { chunk = child; break }
2496 n -= sz;
2497 }
2498 }
2499 return chunk.lines[n]
2500 }
2501
2502 // Get the part of a document between two positions, as an array of
2503 // strings.
2504 function getBetween(doc, start, end) {
2505 var out = [], n = start.line;
2506 doc.iter(start.line, end.line + 1, function (line) {
2507 var text = line.text;
2508 if (n == end.line) { text = text.slice(0, end.ch); }
2509 if (n == start.line) { text = text.slice(start.ch); }
2510 out.push(text);
2511 ++n;
2512 });
2513 return out
2514 }
2515 // Get the lines between from and to, as array of strings.
2516 function getLines(doc, from, to) {
2517 var out = [];
2518 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
2519 return out
2520 }
2521
2522 // Update the height of a line, propagating the height change
2523 // upwards to parent nodes.
2524 function updateLineHeight(line, height) {
2525 var diff = height - line.height;
2526 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
2527 }
2528
2529 // Given a line object, find its line number by walking up through
2530 // its parent links.
2531 function lineNo(line) {
2532 if (line.parent == null) { return null }
2533 var cur = line.parent, no = indexOf(cur.lines, line);
2534 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2535 for (var i = 0;; ++i) {
2536 if (chunk.children[i] == cur) { break }
2537 no += chunk.children[i].chunkSize();
2538 }
2539 }
2540 return no + cur.first
2541 }
2542
2543 // Find the line at the given vertical position, using the height
2544 // information in the document tree.
2545 function lineAtHeight(chunk, h) {
2546 var n = chunk.first;
2547 outer: do {
2548 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
2549 var child = chunk.children[i$1], ch = child.height;
2550 if (h < ch) { chunk = child; continue outer }
2551 h -= ch;
2552 n += child.chunkSize();
2553 }
2554 return n
2555 } while (!chunk.lines)
2556 var i = 0;
2557 for (; i < chunk.lines.length; ++i) {
2558 var line = chunk.lines[i], lh = line.height;
2559 if (h < lh) { break }
2560 h -= lh;
2561 }
2562 return n + i
2563 }
2564
2565 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
2566
2567 function lineNumberFor(options, i) {
2568 return String(options.lineNumberFormatter(i + options.firstLineNumber))
2569 }
2570
2571 // A Pos instance represents a position within the text.
2572 function Pos(line, ch, sticky) {
2573 if ( sticky === void 0 ) sticky = null;
2574
2575 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
2576 this.line = line;
2577 this.ch = ch;
2578 this.sticky = sticky;
2579 }
2580
2581 // Compare two positions, return 0 if they are the same, a negative
2582 // number when a is less, and a positive number otherwise.
2583 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
2584
2585 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
2586
2587 function copyPos(x) {return Pos(x.line, x.ch)}
2588 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
2589 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
2590
2591 // Most of the external API clips given positions to make sure they
2592 // actually exist within the document.
2593 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
2594 function clipPos(doc, pos) {
2595 if (pos.line < doc.first) { return Pos(doc.first, 0) }
2596 var last = doc.first + doc.size - 1;
2597 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
2598 return clipToLen(pos, getLine(doc, pos.line).text.length)
2599 }
2600 function clipToLen(pos, linelen) {
2601 var ch = pos.ch;
2602 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
2603 else if (ch < 0) { return Pos(pos.line, 0) }
2604 else { return pos }
2605 }
2606 function clipPosArray(doc, array) {
2607 var out = [];
2608 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
2609 return out
2610 }
2611
2612 var SavedContext = function(state, lookAhead) {
2613 this.state = state;
2614 this.lookAhead = lookAhead;
2615 };
2616
2617 var Context = function(doc, state, line, lookAhead) {
2618 this.state = state;
2619 this.doc = doc;
2620 this.line = line;
2621 this.maxLookAhead = lookAhead || 0;
2622 this.baseTokens = null;
2623 this.baseTokenPos = 1;
2624 };
2625
2626 Context.prototype.lookAhead = function (n) {
2627 var line = this.doc.getLine(this.line + n);
2628 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
2629 return line
2630 };
2631
2632 Context.prototype.baseToken = function (n) {
2633 if (!this.baseTokens) { return null }
2634 while (this.baseTokens[this.baseTokenPos] <= n)
2635 { this.baseTokenPos += 2; }
2636 var type = this.baseTokens[this.baseTokenPos + 1];
2637 return {type: type && type.replace(/( |^)overlay .*/, ""),
2638 size: this.baseTokens[this.baseTokenPos] - n}
2639 };
2640
2641 Context.prototype.nextLine = function () {
2642 this.line++;
2643 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
2644 };
2645
2646 Context.fromSaved = function (doc, saved, line) {
2647 if (saved instanceof SavedContext)
2648 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
2649 else
2650 { return new Context(doc, copyState(doc.mode, saved), line) }
2651 };
2652
2653 Context.prototype.save = function (copy) {
2654 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
2655 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
2656 };
2657
2658
2659 // Compute a style array (an array starting with a mode generation
2660 // -- for invalidation -- followed by pairs of end positions and
2661 // style strings), which is used to highlight the tokens on the
2662 // line.
2663 function highlightLine(cm, line, context, forceToEnd) {
2664 // A styles array always starts with a number identifying the
2665 // mode/overlays that it is based on (for easy invalidation).
2666 var st = [cm.state.modeGen], lineClasses = {};
2667 // Compute the base array of styles
2668 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
2669 lineClasses, forceToEnd);
2670 var state = context.state;
2671
2672 // Run overlays, adjust style array.
2673 var loop = function ( o ) {
2674 context.baseTokens = st;
2675 var overlay = cm.state.overlays[o], i = 1, at = 0;
2676 context.state = true;
2677 runMode(cm, line.text, overlay.mode, context, function (end, style) {
2678 var start = i;
2679 // Ensure there's a token end at the current position, and that i points at it
2680 while (at < end) {
2681 var i_end = st[i];
2682 if (i_end > end)
2683 { st.splice(i, 1, end, st[i+1], i_end); }
2684 i += 2;
2685 at = Math.min(end, i_end);
2686 }
2687 if (!style) { return }
2688 if (overlay.opaque) {
2689 st.splice(start, i - start, end, "overlay " + style);
2690 i = start + 2;
2691 } else {
2692 for (; start < i; start += 2) {
2693 var cur = st[start+1];
2694 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
2695 }
2696 }
2697 }, lineClasses);
2698 context.state = state;
2699 context.baseTokens = null;
2700 context.baseTokenPos = 1;
2701 };
2702
2703 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
2704
2705 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
2706 }
2707
2708 function getLineStyles(cm, line, updateFrontier) {
2709 if (!line.styles || line.styles[0] != cm.state.modeGen) {
2710 var context = getContextBefore(cm, lineNo(line));
2711 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
2712 var result = highlightLine(cm, line, context);
2713 if (resetState) { context.state = resetState; }
2714 line.stateAfter = context.save(!resetState);
2715 line.styles = result.styles;
2716 if (result.classes) { line.styleClasses = result.classes; }
2717 else if (line.styleClasses) { line.styleClasses = null; }
2718 if (updateFrontier === cm.doc.highlightFrontier)
2719 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
2720 }
2721 return line.styles
2722 }
2723
2724 function getContextBefore(cm, n, precise) {
2725 var doc = cm.doc, display = cm.display;
2726 if (!doc.mode.startState) { return new Context(doc, true, n) }
2727 var start = findStartLine(cm, n, precise);
2728 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
2729 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
2730
2731 doc.iter(start, n, function (line) {
2732 processLine(cm, line.text, context);
2733 var pos = context.line;
2734 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
2735 context.nextLine();
2736 });
2737 if (precise) { doc.modeFrontier = context.line; }
2738 return context
2739 }
2740
2741 // Lightweight form of highlight -- proceed over this line and
2742 // update state, but don't save a style array. Used for lines that
2743 // aren't currently visible.
2744 function processLine(cm, text, context, startAt) {
2745 var mode = cm.doc.mode;
2746 var stream = new StringStream(text, cm.options.tabSize, context);
2747 stream.start = stream.pos = startAt || 0;
2748 if (text == "") { callBlankLine(mode, context.state); }
2749 while (!stream.eol()) {
2750 readToken(mode, stream, context.state);
2751 stream.start = stream.pos;
2752 }
2753 }
2754
2755 function callBlankLine(mode, state) {
2756 if (mode.blankLine) { return mode.blankLine(state) }
2757 if (!mode.innerMode) { return }
2758 var inner = innerMode(mode, state);
2759 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
2760 }
2761
2762 function readToken(mode, stream, state, inner) {
2763 for (var i = 0; i < 10; i++) {
2764 if (inner) { inner[0] = innerMode(mode, state).mode; }
2765 var style = mode.token(stream, state);
2766 if (stream.pos > stream.start) { return style }
2767 }
2768 throw new Error("Mode " + mode.name + " failed to advance stream.")
2769 }
2770
2771 var Token = function(stream, type, state) {
2772 this.start = stream.start; this.end = stream.pos;
2773 this.string = stream.current();
2774 this.type = type || null;
2775 this.state = state;
2776 };
2777
2778 // Utility for getTokenAt and getLineTokens
2779 function takeToken(cm, pos, precise, asArray) {
2780 var doc = cm.doc, mode = doc.mode, style;
2781 pos = clipPos(doc, pos);
2782 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
2783 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
2784 if (asArray) { tokens = []; }
2785 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
2786 stream.start = stream.pos;
2787 style = readToken(mode, stream, context.state);
2788 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
2789 }
2790 return asArray ? tokens : new Token(stream, style, context.state)
2791 }
2792
2793 function extractLineClasses(type, output) {
2794 if (type) { for (;;) {
2795 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
2796 if (!lineClass) { break }
2797 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
2798 var prop = lineClass[1] ? "bgClass" : "textClass";
2799 if (output[prop] == null)
2800 { output[prop] = lineClass[2]; }
2801 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
2802 { output[prop] += " " + lineClass[2]; }
2803 } }
2804 return type
2805 }
2806
2807 // Run the given mode's parser over a line, calling f for each token.
2808 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
2809 var flattenSpans = mode.flattenSpans;
2810 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
2811 var curStart = 0, curStyle = null;
2812 var stream = new StringStream(text, cm.options.tabSize, context), style;
2813 var inner = cm.options.addModeClass && [null];
2814 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
2815 while (!stream.eol()) {
2816 if (stream.pos > cm.options.maxHighlightLength) {
2817 flattenSpans = false;
2818 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
2819 stream.pos = text.length;
2820 style = null;
2821 } else {
2822 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
2823 }
2824 if (inner) {
2825 var mName = inner[0].name;
2826 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
2827 }
2828 if (!flattenSpans || curStyle != style) {
2829 while (curStart < stream.start) {
2830 curStart = Math.min(stream.start, curStart + 5000);
2831 f(curStart, curStyle);
2832 }
2833 curStyle = style;
2834 }
2835 stream.start = stream.pos;
2836 }
2837 while (curStart < stream.pos) {
2838 // Webkit seems to refuse to render text nodes longer than 57444
2839 // characters, and returns inaccurate measurements in nodes
2840 // starting around 5000 chars.
2841 var pos = Math.min(stream.pos, curStart + 5000);
2842 f(pos, curStyle);
2843 curStart = pos;
2844 }
2845 }
2846
2847 // Finds the line to start with when starting a parse. Tries to
2848 // find a line with a stateAfter, so that it can start with a
2849 // valid state. If that fails, it returns the line with the
2850 // smallest indentation, which tends to need the least context to
2851 // parse correctly.
2852 function findStartLine(cm, n, precise) {
2853 var minindent, minline, doc = cm.doc;
2854 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2855 for (var search = n; search > lim; --search) {
2856 if (search <= doc.first) { return doc.first }
2857 var line = getLine(doc, search - 1), after = line.stateAfter;
2858 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
2859 { return search }
2860 var indented = countColumn(line.text, null, cm.options.tabSize);
2861 if (minline == null || minindent > indented) {
2862 minline = search - 1;
2863 minindent = indented;
2864 }
2865 }
2866 return minline
2867 }
2868
2869 function retreatFrontier(doc, n) {
2870 doc.modeFrontier = Math.min(doc.modeFrontier, n);
2871 if (doc.highlightFrontier < n - 10) { return }
2872 var start = doc.first;
2873 for (var line = n - 1; line > start; line--) {
2874 var saved = getLine(doc, line).stateAfter;
2875 // change is on 3
2876 // state on line 1 looked ahead 2 -- so saw 3
2877 // test 1 + 2 < 3 should cover this
2878 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
2879 start = line + 1;
2880 break
2881 }
2882 }
2883 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
2884 }
2885
2886 // Optimize some code when these features are not used.
2887 var sawReadOnlySpans = false, sawCollapsedSpans = false;
2888
2889 function seeReadOnlySpans() {
2890 sawReadOnlySpans = true;
2891 }
2892
2893 function seeCollapsedSpans() {
2894 sawCollapsedSpans = true;
2895 }
2896
2897 // TEXTMARKER SPANS
2898
2899 function MarkedSpan(marker, from, to) {
2900 this.marker = marker;
2901 this.from = from; this.to = to;
2902 }
2903
2904 // Search an array of spans for a span matching the given marker.
2905 function getMarkedSpanFor(spans, marker) {
2906 if (spans) { for (var i = 0; i < spans.length; ++i) {
2907 var span = spans[i];
2908 if (span.marker == marker) { return span }
2909 } }
2910 }
2911 // Remove a span from an array, returning undefined if no spans are
2912 // left (we don't store arrays for lines without spans).
2913 function removeMarkedSpan(spans, span) {
2914 var r;
2915 for (var i = 0; i < spans.length; ++i)
2916 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2917 return r
2918 }
2919 // Add a span to a line.
2920 function addMarkedSpan(line, span) {
2921 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2922 span.marker.attachLine(line);
2923 }
2924
2925 // Used for the algorithm that adjusts markers for a change in the
2926 // document. These functions cut an array of spans at a given
2927 // character position, returning an array of remaining chunks (or
2928 // undefined if nothing remains).
2929 function markedSpansBefore(old, startCh, isInsert) {
2930 var nw;
2931 if (old) { for (var i = 0; i < old.length; ++i) {
2932 var span = old[i], marker = span.marker;
2933 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2934 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2935 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
2936 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
2937 }
2938 } }
2939 return nw
2940 }
2941 function markedSpansAfter(old, endCh, isInsert) {
2942 var nw;
2943 if (old) { for (var i = 0; i < old.length; ++i) {
2944 var span = old[i], marker = span.marker;
2945 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2946 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2947 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
2948 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2949 span.to == null ? null : span.to - endCh));
2950 }
2951 } }
2952 return nw
2953 }
2954
2955 // Given a change object, compute the new set of marker spans that
2956 // cover the line in which the change took place. Removes spans
2957 // entirely within the change, reconnects spans belonging to the
2958 // same marker that appear on both sides of the change, and cuts off
2959 // spans partially within the change. Returns an array of span
2960 // arrays with one element for each line in (after) the change.
2961 function stretchSpansOverChange(doc, change) {
2962 if (change.full) { return null }
2963 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2964 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2965 if (!oldFirst && !oldLast) { return null }
2966
2967 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2968 // Get the spans that 'stick out' on both sides
2969 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2970 var last = markedSpansAfter(oldLast, endCh, isInsert);
2971
2972 // Next, merge those two ends
2973 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2974 if (first) {
2975 // Fix up .to properties of first
2976 for (var i = 0; i < first.length; ++i) {
2977 var span = first[i];
2978 if (span.to == null) {
2979 var found = getMarkedSpanFor(last, span.marker);
2980 if (!found) { span.to = startCh; }
2981 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2982 }
2983 }
2984 }
2985 if (last) {
2986 // Fix up .from in last (or move them into first in case of sameLine)
2987 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2988 var span$1 = last[i$1];
2989 if (span$1.to != null) { span$1.to += offset; }
2990 if (span$1.from == null) {
2991 var found$1 = getMarkedSpanFor(first, span$1.marker);
2992 if (!found$1) {
2993 span$1.from = offset;
2994 if (sameLine) { (first || (first = [])).push(span$1); }
2995 }
2996 } else {
2997 span$1.from += offset;
2998 if (sameLine) { (first || (first = [])).push(span$1); }
2999 }
3000 }
3001 }
3002 // Make sure we didn't create any zero-length spans
3003 if (first) { first = clearEmptySpans(first); }
3004 if (last && last != first) { last = clearEmptySpans(last); }
3005
3006 var newMarkers = [first];
3007 if (!sameLine) {
3008 // Fill gap with whole-line-spans
3009 var gap = change.text.length - 2, gapMarkers;
3010 if (gap > 0 && first)
3011 { for (var i$2 = 0; i$2 < first.length; ++i$2)
3012 { if (first[i$2].to == null)
3013 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
3014 for (var i$3 = 0; i$3 < gap; ++i$3)
3015 { newMarkers.push(gapMarkers); }
3016 newMarkers.push(last);
3017 }
3018 return newMarkers
3019 }
3020
3021 // Remove spans that are empty and don't have a clearWhenEmpty
3022 // option of false.
3023 function clearEmptySpans(spans) {
3024 for (var i = 0; i < spans.length; ++i) {
3025 var span = spans[i];
3026 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
3027 { spans.splice(i--, 1); }
3028 }
3029 if (!spans.length) { return null }
3030 return spans
3031 }
3032
3033 // Used to 'clip' out readOnly ranges when making a change.
3034 function removeReadOnlyRanges(doc, from, to) {
3035 var markers = null;
3036 doc.iter(from.line, to.line + 1, function (line) {
3037 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
3038 var mark = line.markedSpans[i].marker;
3039 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3040 { (markers || (markers = [])).push(mark); }
3041 } }
3042 });
3043 if (!markers) { return null }
3044 var parts = [{from: from, to: to}];
3045 for (var i = 0; i < markers.length; ++i) {
3046 var mk = markers[i], m = mk.find(0);
3047 for (var j = 0; j < parts.length; ++j) {
3048 var p = parts[j];
3049 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
3050 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
3051 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
3052 { newParts.push({from: p.from, to: m.from}); }
3053 if (dto > 0 || !mk.inclusiveRight && !dto)
3054 { newParts.push({from: m.to, to: p.to}); }
3055 parts.splice.apply(parts, newParts);
3056 j += newParts.length - 3;
3057 }
3058 }
3059 return parts
3060 }
3061
3062 // Connect or disconnect spans from a line.
3063 function detachMarkedSpans(line) {
3064 var spans = line.markedSpans;
3065 if (!spans) { return }
3066 for (var i = 0; i < spans.length; ++i)
3067 { spans[i].marker.detachLine(line); }
3068 line.markedSpans = null;
3069 }
3070 function attachMarkedSpans(line, spans) {
3071 if (!spans) { return }
3072 for (var i = 0; i < spans.length; ++i)
3073 { spans[i].marker.attachLine(line); }
3074 line.markedSpans = spans;
3075 }
3076
3077 // Helpers used when computing which overlapping collapsed span
3078 // counts as the larger one.
3079 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
3080 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
3081
3082 // Returns a number indicating which of two overlapping collapsed
3083 // spans is larger (and thus includes the other). Falls back to
3084 // comparing ids when the spans cover exactly the same range.
3085 function compareCollapsedMarkers(a, b) {
3086 var lenDiff = a.lines.length - b.lines.length;
3087 if (lenDiff != 0) { return lenDiff }
3088 var aPos = a.find(), bPos = b.find();
3089 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
3090 if (fromCmp) { return -fromCmp }
3091 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
3092 if (toCmp) { return toCmp }
3093 return b.id - a.id
3094 }
3095
3096 // Find out whether a line ends or starts in a collapsed span. If
3097 // so, return the marker for that span.
3098 function collapsedSpanAtSide(line, start) {
3099 var sps = sawCollapsedSpans && line.markedSpans, found;
3100 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
3101 sp = sps[i];
3102 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
3103 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
3104 { found = sp.marker; }
3105 } }
3106 return found
3107 }
3108 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
3109 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
3110
3111 function collapsedSpanAround(line, ch) {
3112 var sps = sawCollapsedSpans && line.markedSpans, found;
3113 if (sps) { for (var i = 0; i < sps.length; ++i) {
3114 var sp = sps[i];
3115 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
3116 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
3117 } }
3118 return found
3119 }
3120
3121 // Test whether there exists a collapsed span that partially
3122 // overlaps (covers the start or end, but not both) of a new span.
3123 // Such overlap is not allowed.
3124 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
3125 var line = getLine(doc, lineNo);
3126 var sps = sawCollapsedSpans && line.markedSpans;
3127 if (sps) { for (var i = 0; i < sps.length; ++i) {
3128 var sp = sps[i];
3129 if (!sp.marker.collapsed) { continue }
3130 var found = sp.marker.find(0);
3131 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
3132 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
3133 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
3134 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
3135 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
3136 { return true }
3137 } }
3138 }
3139
3140 // A visual line is a line as drawn on the screen. Folding, for
3141 // example, can cause multiple logical lines to appear on the same
3142 // visual line. This finds the start of the visual line that the
3143 // given line is part of (usually that is the line itself).
3144 function visualLine(line) {
3145 var merged;
3146 while (merged = collapsedSpanAtStart(line))
3147 { line = merged.find(-1, true).line; }
3148 return line
3149 }
3150
3151 function visualLineEnd(line) {
3152 var merged;
3153 while (merged = collapsedSpanAtEnd(line))
3154 { line = merged.find(1, true).line; }
3155 return line
3156 }
3157
3158 // Returns an array of logical lines that continue the visual line
3159 // started by the argument, or undefined if there are no such lines.
3160 function visualLineContinued(line) {
3161 var merged, lines;
3162 while (merged = collapsedSpanAtEnd(line)) {
3163 line = merged.find(1, true).line
3164 ;(lines || (lines = [])).push(line);
3165 }
3166 return lines
3167 }
3168
3169 // Get the line number of the start of the visual line that the
3170 // given line number is part of.
3171 function visualLineNo(doc, lineN) {
3172 var line = getLine(doc, lineN), vis = visualLine(line);
3173 if (line == vis) { return lineN }
3174 return lineNo(vis)
3175 }
3176
3177 // Get the line number of the start of the next visual line after
3178 // the given line.
3179 function visualLineEndNo(doc, lineN) {
3180 if (lineN > doc.lastLine()) { return lineN }
3181 var line = getLine(doc, lineN), merged;
3182 if (!lineIsHidden(doc, line)) { return lineN }
3183 while (merged = collapsedSpanAtEnd(line))
3184 { line = merged.find(1, true).line; }
3185 return lineNo(line) + 1
3186 }
3187
3188 // Compute whether a line is hidden. Lines count as hidden when they
3189 // are part of a visual line that starts with another line, or when
3190 // they are entirely covered by collapsed, non-widget span.
3191 function lineIsHidden(doc, line) {
3192 var sps = sawCollapsedSpans && line.markedSpans;
3193 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
3194 sp = sps[i];
3195 if (!sp.marker.collapsed) { continue }
3196 if (sp.from == null) { return true }
3197 if (sp.marker.widgetNode) { continue }
3198 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3199 { return true }
3200 } }
3201 }
3202 function lineIsHiddenInner(doc, line, span) {
3203 if (span.to == null) {
3204 var end = span.marker.find(1, true);
3205 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
3206 }
3207 if (span.marker.inclusiveRight && span.to == line.text.length)
3208 { return true }
3209 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
3210 sp = line.markedSpans[i];
3211 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
3212 (sp.to == null || sp.to != span.from) &&
3213 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3214 lineIsHiddenInner(doc, line, sp)) { return true }
3215 }
3216 }
3217
3218 // Find the height above the given line.
3219 function heightAtLine(lineObj) {
3220 lineObj = visualLine(lineObj);
3221
3222 var h = 0, chunk = lineObj.parent;
3223 for (var i = 0; i < chunk.lines.length; ++i) {
3224 var line = chunk.lines[i];
3225 if (line == lineObj) { break }
3226 else { h += line.height; }
3227 }
3228 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
3229 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
3230 var cur = p.children[i$1];
3231 if (cur == chunk) { break }
3232 else { h += cur.height; }
3233 }
3234 }
3235 return h
3236 }
3237
3238 // Compute the character length of a line, taking into account
3239 // collapsed ranges (see markText) that might hide parts, and join
3240 // other lines onto it.
3241 function lineLength(line) {
3242 if (line.height == 0) { return 0 }
3243 var len = line.text.length, merged, cur = line;
3244 while (merged = collapsedSpanAtStart(cur)) {
3245 var found = merged.find(0, true);
3246 cur = found.from.line;
3247 len += found.from.ch - found.to.ch;
3248 }
3249 cur = line;
3250 while (merged = collapsedSpanAtEnd(cur)) {
3251 var found$1 = merged.find(0, true);
3252 len -= cur.text.length - found$1.from.ch;
3253 cur = found$1.to.line;
3254 len += cur.text.length - found$1.to.ch;
3255 }
3256 return len
3257 }
3258
3259 // Find the longest line in the document.
3260 function findMaxLine(cm) {
3261 var d = cm.display, doc = cm.doc;
3262 d.maxLine = getLine(doc, doc.first);
3263 d.maxLineLength = lineLength(d.maxLine);
3264 d.maxLineChanged = true;
3265 doc.iter(function (line) {
3266 var len = lineLength(line);
3267 if (len > d.maxLineLength) {
3268 d.maxLineLength = len;
3269 d.maxLine = line;
3270 }
3271 });
3272 }
3273
3274 // LINE DATA STRUCTURE
3275
3276 // Line objects. These hold state related to a line, including
3277 // highlighting info (the styles array).
3278 var Line = function(text, markedSpans, estimateHeight) {
3279 this.text = text;
3280 attachMarkedSpans(this, markedSpans);
3281 this.height = estimateHeight ? estimateHeight(this) : 1;
3282 };
3283
3284 Line.prototype.lineNo = function () { return lineNo(this) };
3285 eventMixin(Line);
3286
3287 // Change the content (text, markers) of a line. Automatically
3288 // invalidates cached information and tries to re-estimate the
3289 // line's height.
3290 function updateLine(line, text, markedSpans, estimateHeight) {
3291 line.text = text;
3292 if (line.stateAfter) { line.stateAfter = null; }
3293 if (line.styles) { line.styles = null; }
3294 if (line.order != null) { line.order = null; }
3295 detachMarkedSpans(line);
3296 attachMarkedSpans(line, markedSpans);
3297 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3298 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3299 }
3300
3301 // Detach a line from the document tree and its markers.
3302 function cleanUpLine(line) {
3303 line.parent = null;
3304 detachMarkedSpans(line);
3305 }
3306
3307 // Convert a style as returned by a mode (either null, or a string
3308 // containing one or more styles) to a CSS style. This is cached,
3309 // and also looks for line-wide styles.
3310 var styleToClassCache = {}, styleToClassCacheWithMode = {};
3311 function interpretTokenStyle(style, options) {
3312 if (!style || /^\s*$/.test(style)) { return null }
3313 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3314 return cache[style] ||
3315 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3316 }
3317
3318 // Render the DOM representation of the text of a line. Also builds
3319 // up a 'line map', which points at the DOM nodes that represent
3320 // specific stretches of text, and is used by the measuring code.
3321 // The returned object contains the DOM node, this map, and
3322 // information about line-wide styles that were set by the mode.
3323 function buildLineContent(cm, lineView) {
3324 // The padding-right forces the element to have a 'border', which
3325 // is needed on Webkit to be able to get line-level bounding
3326 // rectangles for it (in measureChar).
3327 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3328 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3329 col: 0, pos: 0, cm: cm,
3330 trailingSpace: false,
3331 splitSpaces: cm.getOption("lineWrapping")};
3332 lineView.measure = {};
3333
3334 // Iterate over the logical lines that make up this visual line.
3335 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3336 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3337 builder.pos = 0;
3338 builder.addToken = buildToken;
3339 // Optionally wire in some hacks into the token-rendering
3340 // algorithm, to deal with browser quirks.
3341 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3342 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3343 builder.map = [];
3344 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3345 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3346 if (line.styleClasses) {
3347 if (line.styleClasses.bgClass)
3348 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3349 if (line.styleClasses.textClass)
3350 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3351 }
3352
3353 // Ensure at least a single node is present, for measuring.
3354 if (builder.map.length == 0)
3355 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3356
3357 // Store the map and a cache object for the current logical line
3358 if (i == 0) {
3359 lineView.measure.map = builder.map;
3360 lineView.measure.cache = {};
3361 } else {
3362 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3363 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3364 }
3365 }
3366
3367 // See issue #2901
3368 if (webkit) {
3369 var last = builder.content.lastChild;
3370 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3371 { builder.content.className = "cm-tab-wrap-hack"; }
3372 }
3373
3374 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3375 if (builder.pre.className)
3376 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3377
3378 return builder
3379 }
3380
3381 function defaultSpecialCharPlaceholder(ch) {
3382 var token = elt("span", "\u2022", "cm-invalidchar");
3383 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3384 token.setAttribute("aria-label", token.title);
3385 return token
3386 }
3387
3388 // Build up the DOM representation for a single token, and add it to
3389 // the line map. Takes care to render special characters separately.
3390 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
3391 if (!text) { return }
3392 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3393 var special = builder.cm.state.specialChars, mustWrap = false;
3394 var content;
3395 if (!special.test(text)) {
3396 builder.col += text.length;
3397 content = document.createTextNode(displayText);
3398 builder.map.push(builder.pos, builder.pos + text.length, content);
3399 if (ie && ie_version < 9) { mustWrap = true; }
3400 builder.pos += text.length;
3401 } else {
3402 content = document.createDocumentFragment();
3403 var pos = 0;
3404 while (true) {
3405 special.lastIndex = pos;
3406 var m = special.exec(text);
3407 var skipped = m ? m.index - pos : text.length - pos;
3408 if (skipped) {
3409 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3410 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3411 else { content.appendChild(txt); }
3412 builder.map.push(builder.pos, builder.pos + skipped, txt);
3413 builder.col += skipped;
3414 builder.pos += skipped;
3415 }
3416 if (!m) { break }
3417 pos += skipped + 1;
3418 var txt$1 = (void 0);
3419 if (m[0] == "\t") {
3420 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3421 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3422 txt$1.setAttribute("role", "presentation");
3423 txt$1.setAttribute("cm-text", "\t");
3424 builder.col += tabWidth;
3425 } else if (m[0] == "\r" || m[0] == "\n") {
3426 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3427 txt$1.setAttribute("cm-text", m[0]);
3428 builder.col += 1;
3429 } else {
3430 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3431 txt$1.setAttribute("cm-text", m[0]);
3432 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3433 else { content.appendChild(txt$1); }
3434 builder.col += 1;
3435 }
3436 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3437 builder.pos++;
3438 }
3439 }
3440 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3441 if (style || startStyle || endStyle || mustWrap || css) {
3442 var fullStyle = style || "";
3443 if (startStyle) { fullStyle += startStyle; }
3444 if (endStyle) { fullStyle += endStyle; }
3445 var token = elt("span", [content], fullStyle, css);
3446 if (attributes) {
3447 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
3448 { token.setAttribute(attr, attributes[attr]); } }
3449 }
3450 return builder.content.appendChild(token)
3451 }
3452 builder.content.appendChild(content);
3453 }
3454
3455 // Change some spaces to NBSP to prevent the browser from collapsing
3456 // trailing spaces at the end of a line when rendering text (issue #1362).
3457 function splitSpaces(text, trailingBefore) {
3458 if (text.length > 1 && !/ /.test(text)) { return text }
3459 var spaceBefore = trailingBefore, result = "";
3460 for (var i = 0; i < text.length; i++) {
3461 var ch = text.charAt(i);
3462 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3463 { ch = "\u00a0"; }
3464 result += ch;
3465 spaceBefore = ch == " ";
3466 }
3467 return result
3468 }
3469
3470 // Work around nonsense dimensions being reported for stretches of
3471 // right-to-left text.
3472 function buildTokenBadBidi(inner, order) {
3473 return function (builder, text, style, startStyle, endStyle, css, attributes) {
3474 style = style ? style + " cm-force-border" : "cm-force-border";
3475 var start = builder.pos, end = start + text.length;
3476 for (;;) {
3477 // Find the part that overlaps with the start of this text
3478 var part = (void 0);
3479 for (var i = 0; i < order.length; i++) {
3480 part = order[i];
3481 if (part.to > start && part.from <= start) { break }
3482 }
3483 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
3484 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
3485 startStyle = null;
3486 text = text.slice(part.to - start);
3487 start = part.to;
3488 }
3489 }
3490 }
3491
3492 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3493 var widget = !ignoreWidget && marker.widgetNode;
3494 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3495 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3496 if (!widget)
3497 { widget = builder.content.appendChild(document.createElement("span")); }
3498 widget.setAttribute("cm-marker", marker.id);
3499 }
3500 if (widget) {
3501 builder.cm.display.input.setUneditable(widget);
3502 builder.content.appendChild(widget);
3503 }
3504 builder.pos += size;
3505 builder.trailingSpace = false;
3506 }
3507
3508 // Outputs a number of spans to make up a line, taking highlighting
3509 // and marked text into account.
3510 function insertLineContent(line, builder, styles) {
3511 var spans = line.markedSpans, allText = line.text, at = 0;
3512 if (!spans) {
3513 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3514 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3515 return
3516 }
3517
3518 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3519 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
3520 for (;;) {
3521 if (nextChange == pos) { // Update current marker set
3522 spanStyle = spanEndStyle = spanStartStyle = css = "";
3523 attributes = null;
3524 collapsed = null; nextChange = Infinity;
3525 var foundBookmarks = [], endStyles = (void 0);
3526 for (var j = 0; j < spans.length; ++j) {
3527 var sp = spans[j], m = sp.marker;
3528 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3529 foundBookmarks.push(m);
3530 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3531 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3532 nextChange = sp.to;
3533 spanEndStyle = "";
3534 }
3535 if (m.className) { spanStyle += " " + m.className; }
3536 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3537 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3538 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3539 // support for the old title property
3540 // https://github.com/codemirror/CodeMirror/pull/5673
3541 if (m.title) { (attributes || (attributes = {})).title = m.title; }
3542 if (m.attributes) {
3543 for (var attr in m.attributes)
3544 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
3545 }
3546 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3547 { collapsed = sp; }
3548 } else if (sp.from > pos && nextChange > sp.from) {
3549 nextChange = sp.from;
3550 }
3551 }
3552 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3553 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3554
3555 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3556 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3557 if (collapsed && (collapsed.from || 0) == pos) {
3558 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3559 collapsed.marker, collapsed.from == null);
3560 if (collapsed.to == null) { return }
3561 if (collapsed.to == pos) { collapsed = false; }
3562 }
3563 }
3564 if (pos >= len) { break }
3565
3566 var upto = Math.min(len, nextChange);
3567 while (true) {
3568 if (text) {
3569 var end = pos + text.length;
3570 if (!collapsed) {
3571 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3572 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3573 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
3574 }
3575 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3576 pos = end;
3577 spanStartStyle = "";
3578 }
3579 text = allText.slice(at, at = styles[i++]);
3580 style = interpretTokenStyle(styles[i++], builder.cm.options);
3581 }
3582 }
3583 }
3584
3585
3586 // These objects are used to represent the visible (currently drawn)
3587 // part of the document. A LineView may correspond to multiple
3588 // logical lines, if those are connected by collapsed ranges.
3589 function LineView(doc, line, lineN) {
3590 // The starting line
3591 this.line = line;
3592 // Continuing lines, if any
3593 this.rest = visualLineContinued(line);
3594 // Number of logical lines in this visual line
3595 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3596 this.node = this.text = null;
3597 this.hidden = lineIsHidden(doc, line);
3598 }
3599
3600 // Create a range of LineView objects for the given lines.
3601 function buildViewArray(cm, from, to) {
3602 var array = [], nextPos;
3603 for (var pos = from; pos < to; pos = nextPos) {
3604 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3605 nextPos = pos + view.size;
3606 array.push(view);
3607 }
3608 return array
3609 }
3610
3611 var operationGroup = null;
3612
3613 function pushOperation(op) {
3614 if (operationGroup) {
3615 operationGroup.ops.push(op);
3616 } else {
3617 op.ownsGroup = operationGroup = {
3618 ops: [op],
3619 delayedCallbacks: []
3620 };
3621 }
3622 }
3623
3624 function fireCallbacksForOps(group) {
3625 // Calls delayed callbacks and cursorActivity handlers until no
3626 // new ones appear
3627 var callbacks = group.delayedCallbacks, i = 0;
3628 do {
3629 for (; i < callbacks.length; i++)
3630 { callbacks[i].call(null); }
3631 for (var j = 0; j < group.ops.length; j++) {
3632 var op = group.ops[j];
3633 if (op.cursorActivityHandlers)
3634 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3635 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3636 }
3637 } while (i < callbacks.length)
3638 }
3639
3640 function finishOperation(op, endCb) {
3641 var group = op.ownsGroup;
3642 if (!group) { return }
3643
3644 try { fireCallbacksForOps(group); }
3645 finally {
3646 operationGroup = null;
3647 endCb(group);
3648 }
3649 }
3650
3651 var orphanDelayedCallbacks = null;
3652
3653 // Often, we want to signal events at a point where we are in the
3654 // middle of some work, but don't want the handler to start calling
3655 // other methods on the editor, which might be in an inconsistent
3656 // state or simply not expect any other events to happen.
3657 // signalLater looks whether there are any handlers, and schedules
3658 // them to be executed when the last operation ends, or, if no
3659 // operation is active, when a timeout fires.
3660 function signalLater(emitter, type /*, values...*/) {
3661 var arr = getHandlers(emitter, type);
3662 if (!arr.length) { return }
3663 var args = Array.prototype.slice.call(arguments, 2), list;
3664 if (operationGroup) {
3665 list = operationGroup.delayedCallbacks;
3666 } else if (orphanDelayedCallbacks) {
3667 list = orphanDelayedCallbacks;
3668 } else {
3669 list = orphanDelayedCallbacks = [];
3670 setTimeout(fireOrphanDelayed, 0);
3671 }
3672 var loop = function ( i ) {
3673 list.push(function () { return arr[i].apply(null, args); });
3674 };
3675
3676 for (var i = 0; i < arr.length; ++i)
3677 loop( i );
3678 }
3679
3680 function fireOrphanDelayed() {
3681 var delayed = orphanDelayedCallbacks;
3682 orphanDelayedCallbacks = null;
3683 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3684 }
3685
3686 // When an aspect of a line changes, a string is added to
3687 // lineView.changes. This updates the relevant part of the line's
3688 // DOM structure.
3689 function updateLineForChanges(cm, lineView, lineN, dims) {
3690 for (var j = 0; j < lineView.changes.length; j++) {
3691 var type = lineView.changes[j];
3692 if (type == "text") { updateLineText(cm, lineView); }
3693 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3694 else if (type == "class") { updateLineClasses(cm, lineView); }
3695 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3696 }
3697 lineView.changes = null;
3698 }
3699
3700 // Lines with gutter elements, widgets or a background class need to
3701 // be wrapped, and have the extra elements added to the wrapper div
3702 function ensureLineWrapped(lineView) {
3703 if (lineView.node == lineView.text) {
3704 lineView.node = elt("div", null, null, "position: relative");
3705 if (lineView.text.parentNode)
3706 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3707 lineView.node.appendChild(lineView.text);
3708 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3709 }
3710 return lineView.node
3711 }
3712
3713 function updateLineBackground(cm, lineView) {
3714 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3715 if (cls) { cls += " CodeMirror-linebackground"; }
3716 if (lineView.background) {
3717 if (cls) { lineView.background.className = cls; }
3718 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3719 } else if (cls) {
3720 var wrap = ensureLineWrapped(lineView);
3721 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3722 cm.display.input.setUneditable(lineView.background);
3723 }
3724 }
3725
3726 // Wrapper around buildLineContent which will reuse the structure
3727 // in display.externalMeasured when possible.
3728 function getLineContent(cm, lineView) {
3729 var ext = cm.display.externalMeasured;
3730 if (ext && ext.line == lineView.line) {
3731 cm.display.externalMeasured = null;
3732 lineView.measure = ext.measure;
3733 return ext.built
3734 }
3735 return buildLineContent(cm, lineView)
3736 }
3737
3738 // Redraw the line's text. Interacts with the background and text
3739 // classes because the mode may output tokens that influence these
3740 // classes.
3741 function updateLineText(cm, lineView) {
3742 var cls = lineView.text.className;
3743 var built = getLineContent(cm, lineView);
3744 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3745 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3746 lineView.text = built.pre;
3747 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3748 lineView.bgClass = built.bgClass;
3749 lineView.textClass = built.textClass;
3750 updateLineClasses(cm, lineView);
3751 } else if (cls) {
3752 lineView.text.className = cls;
3753 }
3754 }
3755
3756 function updateLineClasses(cm, lineView) {
3757 updateLineBackground(cm, lineView);
3758 if (lineView.line.wrapClass)
3759 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3760 else if (lineView.node != lineView.text)
3761 { lineView.node.className = ""; }
3762 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3763 lineView.text.className = textClass || "";
3764 }
3765
3766 function updateLineGutter(cm, lineView, lineN, dims) {
3767 if (lineView.gutter) {
3768 lineView.node.removeChild(lineView.gutter);
3769 lineView.gutter = null;
3770 }
3771 if (lineView.gutterBackground) {
3772 lineView.node.removeChild(lineView.gutterBackground);
3773 lineView.gutterBackground = null;
3774 }
3775 if (lineView.line.gutterClass) {
3776 var wrap = ensureLineWrapped(lineView);
3777 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3778 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3779 cm.display.input.setUneditable(lineView.gutterBackground);
3780 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3781 }
3782 var markers = lineView.line.gutterMarkers;
3783 if (cm.options.lineNumbers || markers) {
3784 var wrap$1 = ensureLineWrapped(lineView);
3785 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3786 cm.display.input.setUneditable(gutterWrap);
3787 wrap$1.insertBefore(gutterWrap, lineView.text);
3788 if (lineView.line.gutterClass)
3789 { gutterWrap.className += " " + lineView.line.gutterClass; }
3790 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3791 { lineView.lineNumber = gutterWrap.appendChild(
3792 elt("div", lineNumberFor(cm.options, lineN),
3793 "CodeMirror-linenumber CodeMirror-gutter-elt",
3794 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3795 if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
3796 var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
3797 if (found)
3798 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3799 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3800 } }
3801 }
3802 }
3803
3804 function updateLineWidgets(cm, lineView, dims) {
3805 if (lineView.alignable) { lineView.alignable = null; }
3806 var isWidget = classTest("CodeMirror-linewidget");
3807 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3808 next = node.nextSibling;
3809 if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
3810 }
3811 insertLineWidgets(cm, lineView, dims);
3812 }
3813
3814 // Build a line's DOM representation from scratch
3815 function buildLineElement(cm, lineView, lineN, dims) {
3816 var built = getLineContent(cm, lineView);
3817 lineView.text = lineView.node = built.pre;
3818 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3819 if (built.textClass) { lineView.textClass = built.textClass; }
3820
3821 updateLineClasses(cm, lineView);
3822 updateLineGutter(cm, lineView, lineN, dims);
3823 insertLineWidgets(cm, lineView, dims);
3824 return lineView.node
3825 }
3826
3827 // A lineView may contain multiple logical lines (when merged by
3828 // collapsed spans). The widgets for all of them need to be drawn.
3829 function insertLineWidgets(cm, lineView, dims) {
3830 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3831 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3832 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3833 }
3834
3835 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3836 if (!line.widgets) { return }
3837 var wrap = ensureLineWrapped(lineView);
3838 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3839 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
3840 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3841 positionLineWidget(widget, node, lineView, dims);
3842 cm.display.input.setUneditable(node);
3843 if (allowAbove && widget.above)
3844 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3845 else
3846 { wrap.appendChild(node); }
3847 signalLater(widget, "redraw");
3848 }
3849 }
3850
3851 function positionLineWidget(widget, node, lineView, dims) {
3852 if (widget.noHScroll) {
3853 (lineView.alignable || (lineView.alignable = [])).push(node);
3854 var width = dims.wrapperWidth;
3855 node.style.left = dims.fixedPos + "px";
3856 if (!widget.coverGutter) {
3857 width -= dims.gutterTotalWidth;
3858 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3859 }
3860 node.style.width = width + "px";
3861 }
3862 if (widget.coverGutter) {
3863 node.style.zIndex = 5;
3864 node.style.position = "relative";
3865 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3866 }
3867 }
3868
3869 function widgetHeight(widget) {
3870 if (widget.height != null) { return widget.height }
3871 var cm = widget.doc.cm;
3872 if (!cm) { return 0 }
3873 if (!contains(document.body, widget.node)) {
3874 var parentStyle = "position: relative;";
3875 if (widget.coverGutter)
3876 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3877 if (widget.noHScroll)
3878 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3879 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3880 }
3881 return widget.height = widget.node.parentNode.offsetHeight
3882 }
3883
3884 // Return true when the given mouse event happened in a widget
3885 function eventInWidget(display, e) {
3886 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3887 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3888 (n.parentNode == display.sizer && n != display.mover))
3889 { return true }
3890 }
3891 }
3892
3893 // POSITION MEASUREMENT
3894
3895 function paddingTop(display) {return display.lineSpace.offsetTop}
3896 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3897 function paddingH(display) {
3898 if (display.cachedPaddingH) { return display.cachedPaddingH }
3899 var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
3900 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3901 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3902 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3903 return data
3904 }
3905
3906 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3907 function displayWidth(cm) {
3908 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3909 }
3910 function displayHeight(cm) {
3911 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3912 }
3913
3914 // Ensure the lineView.wrapping.heights array is populated. This is
3915 // an array of bottom offsets for the lines that make up a drawn
3916 // line. When lineWrapping is on, there might be more than one
3917 // height.
3918 function ensureLineHeights(cm, lineView, rect) {
3919 var wrapping = cm.options.lineWrapping;
3920 var curWidth = wrapping && displayWidth(cm);
3921 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3922 var heights = lineView.measure.heights = [];
3923 if (wrapping) {
3924 lineView.measure.width = curWidth;
3925 var rects = lineView.text.firstChild.getClientRects();
3926 for (var i = 0; i < rects.length - 1; i++) {
3927 var cur = rects[i], next = rects[i + 1];
3928 if (Math.abs(cur.bottom - next.bottom) > 2)
3929 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
3930 }
3931 }
3932 heights.push(rect.bottom - rect.top);
3933 }
3934 }
3935
3936 // Find a line map (mapping character offsets to text nodes) and a
3937 // measurement cache for the given line number. (A line view might
3938 // contain multiple lines when collapsed ranges are present.)
3939 function mapFromLineView(lineView, line, lineN) {
3940 if (lineView.line == line)
3941 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
3942 for (var i = 0; i < lineView.rest.length; i++)
3943 { if (lineView.rest[i] == line)
3944 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
3945 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
3946 { if (lineNo(lineView.rest[i$1]) > lineN)
3947 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
3948 }
3949
3950 // Render a line into the hidden node display.externalMeasured. Used
3951 // when measurement is needed for a line that's not in the viewport.
3952 function updateExternalMeasurement(cm, line) {
3953 line = visualLine(line);
3954 var lineN = lineNo(line);
3955 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
3956 view.lineN = lineN;
3957 var built = view.built = buildLineContent(cm, view);
3958 view.text = built.pre;
3959 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
3960 return view
3961 }
3962
3963 // Get a {top, bottom, left, right} box (in line-local coordinates)
3964 // for a given character.
3965 function measureChar(cm, line, ch, bias) {
3966 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
3967 }
3968
3969 // Find a line view that corresponds to the given line number.
3970 function findViewForLine(cm, lineN) {
3971 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
3972 { return cm.display.view[findViewIndex(cm, lineN)] }
3973 var ext = cm.display.externalMeasured;
3974 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
3975 { return ext }
3976 }
3977
3978 // Measurement can be split in two steps, the set-up work that
3979 // applies to the whole line, and the measurement of the actual
3980 // character. Functions like coordsChar, that need to do a lot of
3981 // measurements in a row, can thus ensure that the set-up work is
3982 // only done once.
3983 function prepareMeasureForLine(cm, line) {
3984 var lineN = lineNo(line);
3985 var view = findViewForLine(cm, lineN);
3986 if (view && !view.text) {
3987 view = null;
3988 } else if (view && view.changes) {
3989 updateLineForChanges(cm, view, lineN, getDimensions(cm));
3990 cm.curOp.forceUpdate = true;
3991 }
3992 if (!view)
3993 { view = updateExternalMeasurement(cm, line); }
3994
3995 var info = mapFromLineView(view, line, lineN);
3996 return {
3997 line: line, view: view, rect: null,
3998 map: info.map, cache: info.cache, before: info.before,
3999 hasHeights: false
4000 }
4001 }
4002
4003 // Given a prepared measurement object, measures the position of an
4004 // actual character (or fetches it from the cache).
4005 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
4006 if (prepared.before) { ch = -1; }
4007 var key = ch + (bias || ""), found;
4008 if (prepared.cache.hasOwnProperty(key)) {
4009 found = prepared.cache[key];
4010 } else {
4011 if (!prepared.rect)
4012 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
4013 if (!prepared.hasHeights) {
4014 ensureLineHeights(cm, prepared.view, prepared.rect);
4015 prepared.hasHeights = true;
4016 }
4017 found = measureCharInner(cm, prepared, ch, bias);
4018 if (!found.bogus) { prepared.cache[key] = found; }
4019 }
4020 return {left: found.left, right: found.right,
4021 top: varHeight ? found.rtop : found.top,
4022 bottom: varHeight ? found.rbottom : found.bottom}
4023 }
4024
4025 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4026
4027 function nodeAndOffsetInLineMap(map, ch, bias) {
4028 var node, start, end, collapse, mStart, mEnd;
4029 // First, search the line map for the text node corresponding to,
4030 // or closest to, the target character.
4031 for (var i = 0; i < map.length; i += 3) {
4032 mStart = map[i];
4033 mEnd = map[i + 1];
4034 if (ch < mStart) {
4035 start = 0; end = 1;
4036 collapse = "left";
4037 } else if (ch < mEnd) {
4038 start = ch - mStart;
4039 end = start + 1;
4040 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
4041 end = mEnd - mStart;
4042 start = end - 1;
4043 if (ch >= mEnd) { collapse = "right"; }
4044 }
4045 if (start != null) {
4046 node = map[i + 2];
4047 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4048 { collapse = bias; }
4049 if (bias == "left" && start == 0)
4050 { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
4051 node = map[(i -= 3) + 2];
4052 collapse = "left";
4053 } }
4054 if (bias == "right" && start == mEnd - mStart)
4055 { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
4056 node = map[(i += 3) + 2];
4057 collapse = "right";
4058 } }
4059 break
4060 }
4061 }
4062 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4063 }
4064
4065 function getUsefulRect(rects, bias) {
4066 var rect = nullRect;
4067 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4068 if ((rect = rects[i]).left != rect.right) { break }
4069 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4070 if ((rect = rects[i$1]).left != rect.right) { break }
4071 } }
4072 return rect
4073 }
4074
4075 function measureCharInner(cm, prepared, ch, bias) {
4076 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4077 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4078
4079 var rect;
4080 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4081 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4082 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4083 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4084 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4085 { rect = node.parentNode.getBoundingClientRect(); }
4086 else
4087 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4088 if (rect.left || rect.right || start == 0) { break }
4089 end = start;
4090 start = start - 1;
4091 collapse = "right";
4092 }
4093 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4094 } else { // If it is a widget, simply get the box for the whole widget.
4095 if (start > 0) { collapse = bias = "right"; }
4096 var rects;
4097 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4098 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4099 else
4100 { rect = node.getBoundingClientRect(); }
4101 }
4102 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4103 var rSpan = node.parentNode.getClientRects()[0];
4104 if (rSpan)
4105 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4106 else
4107 { rect = nullRect; }
4108 }
4109
4110 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4111 var mid = (rtop + rbot) / 2;
4112 var heights = prepared.view.measure.heights;
4113 var i = 0;
4114 for (; i < heights.length - 1; i++)
4115 { if (mid < heights[i]) { break } }
4116 var top = i ? heights[i - 1] : 0, bot = heights[i];
4117 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4118 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4119 top: top, bottom: bot};
4120 if (!rect.left && !rect.right) { result.bogus = true; }
4121 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4122
4123 return result
4124 }
4125
4126 // Work around problem with bounding client rects on ranges being
4127 // returned incorrectly when zoomed on IE10 and below.
4128 function maybeUpdateRectForZooming(measure, rect) {
4129 if (!window.screen || screen.logicalXDPI == null ||
4130 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4131 { return rect }
4132 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4133 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4134 return {left: rect.left * scaleX, right: rect.right * scaleX,
4135 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4136 }
4137
4138 function clearLineMeasurementCacheFor(lineView) {
4139 if (lineView.measure) {
4140 lineView.measure.cache = {};
4141 lineView.measure.heights = null;
4142 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4143 { lineView.measure.caches[i] = {}; } }
4144 }
4145 }
4146
4147 function clearLineMeasurementCache(cm) {
4148 cm.display.externalMeasure = null;
4149 removeChildren(cm.display.lineMeasure);
4150 for (var i = 0; i < cm.display.view.length; i++)
4151 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4152 }
4153
4154 function clearCaches(cm) {
4155 clearLineMeasurementCache(cm);
4156 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4157 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4158 cm.display.lineNumChars = null;
4159 }
4160
4161 function pageScrollX() {
4162 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4163 // which causes page_Offset and bounding client rects to use
4164 // different reference viewports and invalidate our calculations.
4165 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4166 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4167 }
4168 function pageScrollY() {
4169 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4170 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4171 }
4172
4173 function widgetTopHeight(lineObj) {
4174 var height = 0;
4175 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4176 { height += widgetHeight(lineObj.widgets[i]); } } }
4177 return height
4178 }
4179
4180 // Converts a {top, bottom, left, right} box from line-local
4181 // coordinates into another coordinate system. Context may be one of
4182 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4183 // or "page".
4184 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4185 if (!includeWidgets) {
4186 var height = widgetTopHeight(lineObj);
4187 rect.top += height; rect.bottom += height;
4188 }
4189 if (context == "line") { return rect }
4190 if (!context) { context = "local"; }
4191 var yOff = heightAtLine(lineObj);
4192 if (context == "local") { yOff += paddingTop(cm.display); }
4193 else { yOff -= cm.display.viewOffset; }
4194 if (context == "page" || context == "window") {
4195 var lOff = cm.display.lineSpace.getBoundingClientRect();
4196 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4197 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4198 rect.left += xOff; rect.right += xOff;
4199 }
4200 rect.top += yOff; rect.bottom += yOff;
4201 return rect
4202 }
4203
4204 // Coverts a box from "div" coords to another coordinate system.
4205 // Context may be "window", "page", "div", or "local"./null.
4206 function fromCoordSystem(cm, coords, context) {
4207 if (context == "div") { return coords }
4208 var left = coords.left, top = coords.top;
4209 // First move into "page" coordinate system
4210 if (context == "page") {
4211 left -= pageScrollX();
4212 top -= pageScrollY();
4213 } else if (context == "local" || !context) {
4214 var localBox = cm.display.sizer.getBoundingClientRect();
4215 left += localBox.left;
4216 top += localBox.top;
4217 }
4218
4219 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4220 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4221 }
4222
4223 function charCoords(cm, pos, context, lineObj, bias) {
4224 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4225 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4226 }
4227
4228 // Returns a box for a given cursor position, which may have an
4229 // 'other' property containing the position of the secondary cursor
4230 // on a bidi boundary.
4231 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4232 // and after `char - 1` in writing order of `char - 1`
4233 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4234 // and before `char` in writing order of `char`
4235 // Examples (upper-case letters are RTL, lower-case are LTR):
4236 // Pos(0, 1, ...)
4237 // before after
4238 // ab a|b a|b
4239 // aB a|B aB|
4240 // Ab |Ab A|b
4241 // AB B|A B|A
4242 // Every position after the last character on a line is considered to stick
4243 // to the last character on the line.
4244 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4245 lineObj = lineObj || getLine(cm.doc, pos.line);
4246 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4247 function get(ch, right) {
4248 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4249 if (right) { m.left = m.right; } else { m.right = m.left; }
4250 return intoCoordSystem(cm, lineObj, m, context)
4251 }
4252 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4253 if (ch >= lineObj.text.length) {
4254 ch = lineObj.text.length;
4255 sticky = "before";
4256 } else if (ch <= 0) {
4257 ch = 0;
4258 sticky = "after";
4259 }
4260 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4261
4262 function getBidi(ch, partPos, invert) {
4263 var part = order[partPos], right = part.level == 1;
4264 return get(invert ? ch - 1 : ch, right != invert)
4265 }
4266 var partPos = getBidiPartAt(order, ch, sticky);
4267 var other = bidiOther;
4268 var val = getBidi(ch, partPos, sticky == "before");
4269 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4270 return val
4271 }
4272
4273 // Used to cheaply estimate the coordinates for a position. Used for
4274 // intermediate scroll updates.
4275 function estimateCoords(cm, pos) {
4276 var left = 0;
4277 pos = clipPos(cm.doc, pos);
4278 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4279 var lineObj = getLine(cm.doc, pos.line);
4280 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4281 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4282 }
4283
4284 // Positions returned by coordsChar contain some extra information.
4285 // xRel is the relative x position of the input coordinates compared
4286 // to the found position (so xRel > 0 means the coordinates are to
4287 // the right of the character position, for example). When outside
4288 // is true, that means the coordinates lie outside the line's
4289 // vertical range.
4290 function PosWithInfo(line, ch, sticky, outside, xRel) {
4291 var pos = Pos(line, ch, sticky);
4292 pos.xRel = xRel;
4293 if (outside) { pos.outside = outside; }
4294 return pos
4295 }
4296
4297 // Compute the character position closest to the given coordinates.
4298 // Input must be lineSpace-local ("div" coordinate system).
4299 function coordsChar(cm, x, y) {
4300 var doc = cm.doc;
4301 y += cm.display.viewOffset;
4302 if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
4303 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4304 if (lineN > last)
4305 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
4306 if (x < 0) { x = 0; }
4307
4308 var lineObj = getLine(doc, lineN);
4309 for (;;) {
4310 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4311 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
4312 if (!collapsed) { return found }
4313 var rangeEnd = collapsed.find(1);
4314 if (rangeEnd.line == lineN) { return rangeEnd }
4315 lineObj = getLine(doc, lineN = rangeEnd.line);
4316 }
4317 }
4318
4319 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4320 y -= widgetTopHeight(lineObj);
4321 var end = lineObj.text.length;
4322 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4323 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4324 return {begin: begin, end: end}
4325 }
4326
4327 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4328 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4329 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4330 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4331 }
4332
4333 // Returns true if the given side of a box is after the given
4334 // coordinates, in top-to-bottom, left-to-right order.
4335 function boxIsAfter(box, x, y, left) {
4336 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4337 }
4338
4339 function coordsCharInner(cm, lineObj, lineNo, x, y) {
4340 // Move y into line-local coordinate space
4341 y -= heightAtLine(lineObj);
4342 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4343 // When directly calling `measureCharPrepared`, we have to adjust
4344 // for the widgets at this line.
4345 var widgetHeight = widgetTopHeight(lineObj);
4346 var begin = 0, end = lineObj.text.length, ltr = true;
4347
4348 var order = getOrder(lineObj, cm.doc.direction);
4349 // If the line isn't plain left-to-right text, first figure out
4350 // which bidi section the coordinates fall into.
4351 if (order) {
4352 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4353 (cm, lineObj, lineNo, preparedMeasure, order, x, y);
4354 ltr = part.level != 1;
4355 // The awkward -1 offsets are needed because findFirst (called
4356 // on these below) will treat its first bound as inclusive,
4357 // second as exclusive, but we want to actually address the
4358 // characters in the part's range
4359 begin = ltr ? part.from : part.to - 1;
4360 end = ltr ? part.to : part.from - 1;
4361 }
4362
4363 // A binary search to find the first character whose bounding box
4364 // starts after the coordinates. If we run across any whose box wrap
4365 // the coordinates, store that.
4366 var chAround = null, boxAround = null;
4367 var ch = findFirst(function (ch) {
4368 var box = measureCharPrepared(cm, preparedMeasure, ch);
4369 box.top += widgetHeight; box.bottom += widgetHeight;
4370 if (!boxIsAfter(box, x, y, false)) { return false }
4371 if (box.top <= y && box.left <= x) {
4372 chAround = ch;
4373 boxAround = box;
4374 }
4375 return true
4376 }, begin, end);
4377
4378 var baseX, sticky, outside = false;
4379 // If a box around the coordinates was found, use that
4380 if (boxAround) {
4381 // Distinguish coordinates nearer to the left or right side of the box
4382 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4383 ch = chAround + (atStart ? 0 : 1);
4384 sticky = atStart ? "after" : "before";
4385 baseX = atLeft ? boxAround.left : boxAround.right;
4386 } else {
4387 // (Adjust for extended bound, if necessary.)
4388 if (!ltr && (ch == end || ch == begin)) { ch++; }
4389 // To determine which side to associate with, get the box to the
4390 // left of the character and compare it's vertical position to the
4391 // coordinates
4392 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4393 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
4394 "after" : "before";
4395 // Now get accurate coordinates for this place, in order to get a
4396 // base X position
4397 var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
4398 baseX = coords.left;
4399 outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
4400 }
4401
4402 ch = skipExtendingChars(lineObj.text, ch, 1);
4403 return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
4404 }
4405
4406 function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
4407 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4408 // situation, we can take this ordering to correspond to the visual
4409 // ordering. This finds the first part whose end is after the given
4410 // coordinates.
4411 var index = findFirst(function (i) {
4412 var part = order[i], ltr = part.level != 1;
4413 return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
4414 "line", lineObj, preparedMeasure), x, y, true)
4415 }, 0, order.length - 1);
4416 var part = order[index];
4417 // If this isn't the first part, the part's start is also after
4418 // the coordinates, and the coordinates aren't on the same line as
4419 // that start, move one part back.
4420 if (index > 0) {
4421 var ltr = part.level != 1;
4422 var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
4423 "line", lineObj, preparedMeasure);
4424 if (boxIsAfter(start, x, y, true) && start.top > y)
4425 { part = order[index - 1]; }
4426 }
4427 return part
4428 }
4429
4430 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4431 // In a wrapped line, rtl text on wrapping boundaries can do things
4432 // that don't correspond to the ordering in our `order` array at
4433 // all, so a binary search doesn't work, and we want to return a
4434 // part that only spans one line so that the binary search in
4435 // coordsCharInner is safe. As such, we first find the extent of the
4436 // wrapped line, and then do a flat search in which we discard any
4437 // spans that aren't on the line.
4438 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4439 var begin = ref.begin;
4440 var end = ref.end;
4441 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4442 var part = null, closestDist = null;
4443 for (var i = 0; i < order.length; i++) {
4444 var p = order[i];
4445 if (p.from >= end || p.to <= begin) { continue }
4446 var ltr = p.level != 1;
4447 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4448 // Weigh against spans ending before this, so that they are only
4449 // picked if nothing ends after
4450 var dist = endX < x ? x - endX + 1e9 : endX - x;
4451 if (!part || closestDist > dist) {
4452 part = p;
4453 closestDist = dist;
4454 }
4455 }
4456 if (!part) { part = order[order.length - 1]; }
4457 // Clip the part to the wrapped line.
4458 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4459 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4460 return part
4461 }
4462
4463 var measureText;
4464 // Compute the default text height.
4465 function textHeight(display) {
4466 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4467 if (measureText == null) {
4468 measureText = elt("pre", null, "CodeMirror-line-like");
4469 // Measure a bunch of lines, for browsers that compute
4470 // fractional heights.
4471 for (var i = 0; i < 49; ++i) {
4472 measureText.appendChild(document.createTextNode("x"));
4473 measureText.appendChild(elt("br"));
4474 }
4475 measureText.appendChild(document.createTextNode("x"));
4476 }
4477 removeChildrenAndAdd(display.measure, measureText);
4478 var height = measureText.offsetHeight / 50;
4479 if (height > 3) { display.cachedTextHeight = height; }
4480 removeChildren(display.measure);
4481 return height || 1
4482 }
4483
4484 // Compute the default character width.
4485 function charWidth(display) {
4486 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4487 var anchor = elt("span", "xxxxxxxxxx");
4488 var pre = elt("pre", [anchor], "CodeMirror-line-like");
4489 removeChildrenAndAdd(display.measure, pre);
4490 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4491 if (width > 2) { display.cachedCharWidth = width; }
4492 return width || 10
4493 }
4494
4495 // Do a bulk-read of the DOM positions and sizes needed to draw the
4496 // view, so that we don't interleave reading and writing to the DOM.
4497 function getDimensions(cm) {
4498 var d = cm.display, left = {}, width = {};
4499 var gutterLeft = d.gutters.clientLeft;
4500 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4501 var id = cm.display.gutterSpecs[i].className;
4502 left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
4503 width[id] = n.clientWidth;
4504 }
4505 return {fixedPos: compensateForHScroll(d),
4506 gutterTotalWidth: d.gutters.offsetWidth,
4507 gutterLeft: left,
4508 gutterWidth: width,
4509 wrapperWidth: d.wrapper.clientWidth}
4510 }
4511
4512 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4513 // but using getBoundingClientRect to get a sub-pixel-accurate
4514 // result.
4515 function compensateForHScroll(display) {
4516 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4517 }
4518
4519 // Returns a function that estimates the height of a line, to use as
4520 // first approximation until the line becomes visible (and is thus
4521 // properly measurable).
4522 function estimateHeight(cm) {
4523 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4524 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4525 return function (line) {
4526 if (lineIsHidden(cm.doc, line)) { return 0 }
4527
4528 var widgetsHeight = 0;
4529 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4530 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4531 } }
4532
4533 if (wrapping)
4534 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4535 else
4536 { return widgetsHeight + th }
4537 }
4538 }
4539
4540 function estimateLineHeights(cm) {
4541 var doc = cm.doc, est = estimateHeight(cm);
4542 doc.iter(function (line) {
4543 var estHeight = est(line);
4544 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4545 });
4546 }
4547
4548 // Given a mouse event, find the corresponding position. If liberal
4549 // is false, it checks whether a gutter or scrollbar was clicked,
4550 // and returns null if it was. forRect is used by rectangular
4551 // selections, and tries to estimate a character position even for
4552 // coordinates beyond the right of the text.
4553 function posFromMouse(cm, e, liberal, forRect) {
4554 var display = cm.display;
4555 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4556
4557 var x, y, space = display.lineSpace.getBoundingClientRect();
4558 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4559 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4560 catch (e) { return null }
4561 var coords = coordsChar(cm, x, y), line;
4562 if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4563 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4564 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4565 }
4566 return coords
4567 }
4568
4569 // Find the view element corresponding to a given line. Return null
4570 // when the line isn't visible.
4571 function findViewIndex(cm, n) {
4572 if (n >= cm.display.viewTo) { return null }
4573 n -= cm.display.viewFrom;
4574 if (n < 0) { return null }
4575 var view = cm.display.view;
4576 for (var i = 0; i < view.length; i++) {
4577 n -= view[i].size;
4578 if (n < 0) { return i }
4579 }
4580 }
4581
4582 // Updates the display.view data structure for a given change to the
4583 // document. From and to are in pre-change coordinates. Lendiff is
4584 // the amount of lines added or subtracted by the change. This is
4585 // used for changes that span multiple lines, or change the way
4586 // lines are divided into visual lines. regLineChange (below)
4587 // registers single-line changes.
4588 function regChange(cm, from, to, lendiff) {
4589 if (from == null) { from = cm.doc.first; }
4590 if (to == null) { to = cm.doc.first + cm.doc.size; }
4591 if (!lendiff) { lendiff = 0; }
4592
4593 var display = cm.display;
4594 if (lendiff && to < display.viewTo &&
4595 (display.updateLineNumbers == null || display.updateLineNumbers > from))
4596 { display.updateLineNumbers = from; }
4597
4598 cm.curOp.viewChanged = true;
4599
4600 if (from >= display.viewTo) { // Change after
4601 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
4602 { resetView(cm); }
4603 } else if (to <= display.viewFrom) { // Change before
4604 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
4605 resetView(cm);
4606 } else {
4607 display.viewFrom += lendiff;
4608 display.viewTo += lendiff;
4609 }
4610 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
4611 resetView(cm);
4612 } else if (from <= display.viewFrom) { // Top overlap
4613 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
4614 if (cut) {
4615 display.view = display.view.slice(cut.index);
4616 display.viewFrom = cut.lineN;
4617 display.viewTo += lendiff;
4618 } else {
4619 resetView(cm);
4620 }
4621 } else if (to >= display.viewTo) { // Bottom overlap
4622 var cut$1 = viewCuttingPoint(cm, from, from, -1);
4623 if (cut$1) {
4624 display.view = display.view.slice(0, cut$1.index);
4625 display.viewTo = cut$1.lineN;
4626 } else {
4627 resetView(cm);
4628 }
4629 } else { // Gap in the middle
4630 var cutTop = viewCuttingPoint(cm, from, from, -1);
4631 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
4632 if (cutTop && cutBot) {
4633 display.view = display.view.slice(0, cutTop.index)
4634 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
4635 .concat(display.view.slice(cutBot.index));
4636 display.viewTo += lendiff;
4637 } else {
4638 resetView(cm);
4639 }
4640 }
4641
4642 var ext = display.externalMeasured;
4643 if (ext) {
4644 if (to < ext.lineN)
4645 { ext.lineN += lendiff; }
4646 else if (from < ext.lineN + ext.size)
4647 { display.externalMeasured = null; }
4648 }
4649 }
4650
4651 // Register a change to a single line. Type must be one of "text",
4652 // "gutter", "class", "widget"
4653 function regLineChange(cm, line, type) {
4654 cm.curOp.viewChanged = true;
4655 var display = cm.display, ext = cm.display.externalMeasured;
4656 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
4657 { display.externalMeasured = null; }
4658
4659 if (line < display.viewFrom || line >= display.viewTo) { return }
4660 var lineView = display.view[findViewIndex(cm, line)];
4661 if (lineView.node == null) { return }
4662 var arr = lineView.changes || (lineView.changes = []);
4663 if (indexOf(arr, type) == -1) { arr.push(type); }
4664 }
4665
4666 // Clear the view.
4667 function resetView(cm) {
4668 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
4669 cm.display.view = [];
4670 cm.display.viewOffset = 0;
4671 }
4672
4673 function viewCuttingPoint(cm, oldN, newN, dir) {
4674 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
4675 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
4676 { return {index: index, lineN: newN} }
4677 var n = cm.display.viewFrom;
4678 for (var i = 0; i < index; i++)
4679 { n += view[i].size; }
4680 if (n != oldN) {
4681 if (dir > 0) {
4682 if (index == view.length - 1) { return null }
4683 diff = (n + view[index].size) - oldN;
4684 index++;
4685 } else {
4686 diff = n - oldN;
4687 }
4688 oldN += diff; newN += diff;
4689 }
4690 while (visualLineNo(cm.doc, newN) != newN) {
4691 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
4692 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
4693 index += dir;
4694 }
4695 return {index: index, lineN: newN}
4696 }
4697
4698 // Force the view to cover a given range, adding empty view element
4699 // or clipping off existing ones as needed.
4700 function adjustView(cm, from, to) {
4701 var display = cm.display, view = display.view;
4702 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
4703 display.view = buildViewArray(cm, from, to);
4704 display.viewFrom = from;
4705 } else {
4706 if (display.viewFrom > from)
4707 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
4708 else if (display.viewFrom < from)
4709 { display.view = display.view.slice(findViewIndex(cm, from)); }
4710 display.viewFrom = from;
4711 if (display.viewTo < to)
4712 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
4713 else if (display.viewTo > to)
4714 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
4715 }
4716 display.viewTo = to;
4717 }
4718
4719 // Count the number of lines in the view whose DOM representation is
4720 // out of date (or nonexistent).
4721 function countDirtyView(cm) {
4722 var view = cm.display.view, dirty = 0;
4723 for (var i = 0; i < view.length; i++) {
4724 var lineView = view[i];
4725 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
4726 }
4727 return dirty
4728 }
4729
4730 function updateSelection(cm) {
4731 cm.display.input.showSelection(cm.display.input.prepareSelection());
4732 }
4733
4734 function prepareSelection(cm, primary) {
4735 if ( primary === void 0 ) primary = true;
4736
4737 var doc = cm.doc, result = {};
4738 var curFragment = result.cursors = document.createDocumentFragment();
4739 var selFragment = result.selection = document.createDocumentFragment();
4740
4741 for (var i = 0; i < doc.sel.ranges.length; i++) {
4742 if (!primary && i == doc.sel.primIndex) { continue }
4743 var range = doc.sel.ranges[i];
4744 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
4745 var collapsed = range.empty();
4746 if (collapsed || cm.options.showCursorWhenSelecting)
4747 { drawSelectionCursor(cm, range.head, curFragment); }
4748 if (!collapsed)
4749 { drawSelectionRange(cm, range, selFragment); }
4750 }
4751 return result
4752 }
4753
4754 // Draws a cursor for the given range
4755 function drawSelectionCursor(cm, head, output) {
4756 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4757
4758 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4759 cursor.style.left = pos.left + "px";
4760 cursor.style.top = pos.top + "px";
4761 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4762
4763 if (pos.other) {
4764 // Secondary cursor, shown when on a 'jump' in bi-directional text
4765 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4766 otherCursor.style.display = "";
4767 otherCursor.style.left = pos.other.left + "px";
4768 otherCursor.style.top = pos.other.top + "px";
4769 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4770 }
4771 }
4772
4773 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4774
4775 // Draws the given range as a highlighted selection
4776 function drawSelectionRange(cm, range, output) {
4777 var display = cm.display, doc = cm.doc;
4778 var fragment = document.createDocumentFragment();
4779 var padding = paddingH(cm.display), leftSide = padding.left;
4780 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4781 var docLTR = doc.direction == "ltr";
4782
4783 function add(left, top, width, bottom) {
4784 if (top < 0) { top = 0; }
4785 top = Math.round(top);
4786 bottom = Math.round(bottom);
4787 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")));
4788 }
4789
4790 function drawForLine(line, fromArg, toArg) {
4791 var lineObj = getLine(doc, line);
4792 var lineLen = lineObj.text.length;
4793 var start, end;
4794 function coords(ch, bias) {
4795 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4796 }
4797
4798 function wrapX(pos, dir, side) {
4799 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4800 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4801 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4802 return coords(ch, prop)[prop]
4803 }
4804
4805 var order = getOrder(lineObj, doc.direction);
4806 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4807 var ltr = dir == "ltr";
4808 var fromPos = coords(from, ltr ? "left" : "right");
4809 var toPos = coords(to - 1, ltr ? "right" : "left");
4810
4811 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4812 var first = i == 0, last = !order || i == order.length - 1;
4813 if (toPos.top - fromPos.top <= 3) { // Single line
4814 var openLeft = (docLTR ? openStart : openEnd) && first;
4815 var openRight = (docLTR ? openEnd : openStart) && last;
4816 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4817 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4818 add(left, fromPos.top, right - left, fromPos.bottom);
4819 } else { // Multiple lines
4820 var topLeft, topRight, botLeft, botRight;
4821 if (ltr) {
4822 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4823 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4824 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4825 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4826 } else {
4827 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4828 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4829 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4830 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4831 }
4832 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4833 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4834 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4835 }
4836
4837 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4838 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4839 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4840 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4841 });
4842 return {start: start, end: end}
4843 }
4844
4845 var sFrom = range.from(), sTo = range.to();
4846 if (sFrom.line == sTo.line) {
4847 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4848 } else {
4849 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4850 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4851 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4852 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4853 if (singleVLine) {
4854 if (leftEnd.top < rightStart.top - 2) {
4855 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4856 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4857 } else {
4858 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4859 }
4860 }
4861 if (leftEnd.bottom < rightStart.top)
4862 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4863 }
4864
4865 output.appendChild(fragment);
4866 }
4867
4868 // Cursor-blinking
4869 function restartBlink(cm) {
4870 if (!cm.state.focused) { return }
4871 var display = cm.display;
4872 clearInterval(display.blinker);
4873 var on = true;
4874 display.cursorDiv.style.visibility = "";
4875 if (cm.options.cursorBlinkRate > 0)
4876 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4877 cm.options.cursorBlinkRate); }
4878 else if (cm.options.cursorBlinkRate < 0)
4879 { display.cursorDiv.style.visibility = "hidden"; }
4880 }
4881
4882 function ensureFocus(cm) {
4883 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4884 }
4885
4886 function delayBlurEvent(cm) {
4887 cm.state.delayingBlurEvent = true;
4888 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4889 cm.state.delayingBlurEvent = false;
4890 onBlur(cm);
4891 } }, 100);
4892 }
4893
4894 function onFocus(cm, e) {
4895 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4896
4897 if (cm.options.readOnly == "nocursor") { return }
4898 if (!cm.state.focused) {
4899 signal(cm, "focus", cm, e);
4900 cm.state.focused = true;
4901 addClass(cm.display.wrapper, "CodeMirror-focused");
4902 // This test prevents this from firing when a context
4903 // menu is closed (since the input reset would kill the
4904 // select-all detection hack)
4905 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4906 cm.display.input.reset();
4907 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4908 }
4909 cm.display.input.receivedFocus();
4910 }
4911 restartBlink(cm);
4912 }
4913 function onBlur(cm, e) {
4914 if (cm.state.delayingBlurEvent) { return }
4915
4916 if (cm.state.focused) {
4917 signal(cm, "blur", cm, e);
4918 cm.state.focused = false;
4919 rmClass(cm.display.wrapper, "CodeMirror-focused");
4920 }
4921 clearInterval(cm.display.blinker);
4922 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4923 }
4924
4925 // Read the actual heights of the rendered lines, and update their
4926 // stored heights to match.
4927 function updateHeightsInViewport(cm) {
4928 var display = cm.display;
4929 var prevBottom = display.lineDiv.offsetTop;
4930 for (var i = 0; i < display.view.length; i++) {
4931 var cur = display.view[i], wrapping = cm.options.lineWrapping;
4932 var height = (void 0), width = 0;
4933 if (cur.hidden) { continue }
4934 if (ie && ie_version < 8) {
4935 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4936 height = bot - prevBottom;
4937 prevBottom = bot;
4938 } else {
4939 var box = cur.node.getBoundingClientRect();
4940 height = box.bottom - box.top;
4941 // Check that lines don't extend past the right of the current
4942 // editor width
4943 if (!wrapping && cur.text.firstChild)
4944 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
4945 }
4946 var diff = cur.line.height - height;
4947 if (diff > .005 || diff < -.005) {
4948 updateLineHeight(cur.line, height);
4949 updateWidgetHeight(cur.line);
4950 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4951 { updateWidgetHeight(cur.rest[j]); } }
4952 }
4953 if (width > cm.display.sizerWidth) {
4954 var chWidth = Math.ceil(width / charWidth(cm.display));
4955 if (chWidth > cm.display.maxLineLength) {
4956 cm.display.maxLineLength = chWidth;
4957 cm.display.maxLine = cur.line;
4958 cm.display.maxLineChanged = true;
4959 }
4960 }
4961 }
4962 }
4963
4964 // Read and store the height of line widgets associated with the
4965 // given line.
4966 function updateWidgetHeight(line) {
4967 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4968 var w = line.widgets[i], parent = w.node.parentNode;
4969 if (parent) { w.height = parent.offsetHeight; }
4970 } }
4971 }
4972
4973 // Compute the lines that are visible in a given viewport (defaults
4974 // the the current scroll position). viewport may contain top,
4975 // height, and ensure (see op.scrollToPos) properties.
4976 function visibleLines(display, doc, viewport) {
4977 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4978 top = Math.floor(top - paddingTop(display));
4979 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4980
4981 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4982 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4983 // forces those lines into the viewport (if possible).
4984 if (viewport && viewport.ensure) {
4985 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4986 if (ensureFrom < from) {
4987 from = ensureFrom;
4988 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4989 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4990 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4991 to = ensureTo;
4992 }
4993 }
4994 return {from: from, to: Math.max(to, from + 1)}
4995 }
4996
4997 // SCROLLING THINGS INTO VIEW
4998
4999 // If an editor sits on the top or bottom of the window, partially
5000 // scrolled out of view, this ensures that the cursor is visible.
5001 function maybeScrollWindow(cm, rect) {
5002 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
5003
5004 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
5005 if (rect.top + box.top < 0) { doScroll = true; }
5006 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
5007 if (doScroll != null && !phantom) {
5008 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;"));
5009 cm.display.lineSpace.appendChild(scrollNode);
5010 scrollNode.scrollIntoView(doScroll);
5011 cm.display.lineSpace.removeChild(scrollNode);
5012 }
5013 }
5014
5015 // Scroll a given position into view (immediately), verifying that
5016 // it actually became visible (as line heights are accurately
5017 // measured, the position of something may 'drift' during drawing).
5018 function scrollPosIntoView(cm, pos, end, margin) {
5019 if (margin == null) { margin = 0; }
5020 var rect;
5021 if (!cm.options.lineWrapping && pos == end) {
5022 // Set pos and end to the cursor positions around the character pos sticks to
5023 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
5024 // If pos == Pos(_, 0, "before"), pos and end are unchanged
5025 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
5026 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
5027 }
5028 for (var limit = 0; limit < 5; limit++) {
5029 var changed = false;
5030 var coords = cursorCoords(cm, pos);
5031 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
5032 rect = {left: Math.min(coords.left, endCoords.left),
5033 top: Math.min(coords.top, endCoords.top) - margin,
5034 right: Math.max(coords.left, endCoords.left),
5035 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
5036 var scrollPos = calculateScrollPos(cm, rect);
5037 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
5038 if (scrollPos.scrollTop != null) {
5039 updateScrollTop(cm, scrollPos.scrollTop);
5040 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
5041 }
5042 if (scrollPos.scrollLeft != null) {
5043 setScrollLeft(cm, scrollPos.scrollLeft);
5044 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
5045 }
5046 if (!changed) { break }
5047 }
5048 return rect
5049 }
5050
5051 // Scroll a given set of coordinates into view (immediately).
5052 function scrollIntoView(cm, rect) {
5053 var scrollPos = calculateScrollPos(cm, rect);
5054 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
5055 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
5056 }
5057
5058 // Calculate a new scroll position needed to scroll the given
5059 // rectangle into view. Returns an object with scrollTop and
5060 // scrollLeft properties. When these are undefined, the
5061 // vertical/horizontal position does not need to be adjusted.
5062 function calculateScrollPos(cm, rect) {
5063 var display = cm.display, snapMargin = textHeight(cm.display);
5064 if (rect.top < 0) { rect.top = 0; }
5065 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
5066 var screen = displayHeight(cm), result = {};
5067 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
5068 var docBottom = cm.doc.height + paddingVert(display);
5069 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
5070 if (rect.top < screentop) {
5071 result.scrollTop = atTop ? 0 : rect.top;
5072 } else if (rect.bottom > screentop + screen) {
5073 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
5074 if (newTop != screentop) { result.scrollTop = newTop; }
5075 }
5076
5077 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
5078 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
5079 var tooWide = rect.right - rect.left > screenw;
5080 if (tooWide) { rect.right = rect.left + screenw; }
5081 if (rect.left < 10)
5082 { result.scrollLeft = 0; }
5083 else if (rect.left < screenleft)
5084 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
5085 else if (rect.right > screenw + screenleft - 3)
5086 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
5087 return result
5088 }
5089
5090 // Store a relative adjustment to the scroll position in the current
5091 // operation (to be applied when the operation finishes).
5092 function addToScrollTop(cm, top) {
5093 if (top == null) { return }
5094 resolveScrollToPos(cm);
5095 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
5096 }
5097
5098 // Make sure that at the end of the operation the current cursor is
5099 // shown.
5100 function ensureCursorVisible(cm) {
5101 resolveScrollToPos(cm);
5102 var cur = cm.getCursor();
5103 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
5104 }
5105
5106 function scrollToCoords(cm, x, y) {
5107 if (x != null || y != null) { resolveScrollToPos(cm); }
5108 if (x != null) { cm.curOp.scrollLeft = x; }
5109 if (y != null) { cm.curOp.scrollTop = y; }
5110 }
5111
5112 function scrollToRange(cm, range) {
5113 resolveScrollToPos(cm);
5114 cm.curOp.scrollToPos = range;
5115 }
5116
5117 // When an operation has its scrollToPos property set, and another
5118 // scroll action is applied before the end of the operation, this
5119 // 'simulates' scrolling that position into view in a cheap way, so
5120 // that the effect of intermediate scroll commands is not ignored.
5121 function resolveScrollToPos(cm) {
5122 var range = cm.curOp.scrollToPos;
5123 if (range) {
5124 cm.curOp.scrollToPos = null;
5125 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
5126 scrollToCoordsRange(cm, from, to, range.margin);
5127 }
5128 }
5129
5130 function scrollToCoordsRange(cm, from, to, margin) {
5131 var sPos = calculateScrollPos(cm, {
5132 left: Math.min(from.left, to.left),
5133 top: Math.min(from.top, to.top) - margin,
5134 right: Math.max(from.right, to.right),
5135 bottom: Math.max(from.bottom, to.bottom) + margin
5136 });
5137 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
5138 }
5139
5140 // Sync the scrollable area and scrollbars, ensure the viewport
5141 // covers the visible area.
5142 function updateScrollTop(cm, val) {
5143 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
5144 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
5145 setScrollTop(cm, val, true);
5146 if (gecko) { updateDisplaySimple(cm); }
5147 startWorker(cm, 100);
5148 }
5149
5150 function setScrollTop(cm, val, forceScroll) {
5151 val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
5152 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
5153 cm.doc.scrollTop = val;
5154 cm.display.scrollbars.setScrollTop(val);
5155 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
5156 }
5157
5158 // Sync scroller and scrollbar, ensure the gutter elements are
5159 // aligned.
5160 function setScrollLeft(cm, val, isScroller, forceScroll) {
5161 val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
5162 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
5163 cm.doc.scrollLeft = val;
5164 alignHorizontally(cm);
5165 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
5166 cm.display.scrollbars.setScrollLeft(val);
5167 }
5168
5169 // SCROLLBARS
5170
5171 // Prepare DOM reads needed to update the scrollbars. Done in one
5172 // shot to minimize update/measure roundtrips.
5173 function measureForScrollbars(cm) {
5174 var d = cm.display, gutterW = d.gutters.offsetWidth;
5175 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
5176 return {
5177 clientHeight: d.scroller.clientHeight,
5178 viewHeight: d.wrapper.clientHeight,
5179 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5180 viewWidth: d.wrapper.clientWidth,
5181 barLeft: cm.options.fixedGutter ? gutterW : 0,
5182 docHeight: docH,
5183 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5184 nativeBarWidth: d.nativeBarWidth,
5185 gutterWidth: gutterW
5186 }
5187 }
5188
5189 var NativeScrollbars = function(place, scroll, cm) {
5190 this.cm = cm;
5191 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5192 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5193 vert.tabIndex = horiz.tabIndex = -1;
5194 place(vert); place(horiz);
5195
5196 on(vert, "scroll", function () {
5197 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5198 });
5199 on(horiz, "scroll", function () {
5200 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5201 });
5202
5203 this.checkedZeroWidth = false;
5204 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5205 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5206 };
5207
5208 NativeScrollbars.prototype.update = function (measure) {
5209 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5210 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5211 var sWidth = measure.nativeBarWidth;
5212
5213 if (needsV) {
5214 this.vert.style.display = "block";
5215 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5216 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5217 // A bug in IE8 can cause this value to be negative, so guard it.
5218 this.vert.firstChild.style.height =
5219 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5220 } else {
5221 this.vert.style.display = "";
5222 this.vert.firstChild.style.height = "0";
5223 }
5224
5225 if (needsH) {
5226 this.horiz.style.display = "block";
5227 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5228 this.horiz.style.left = measure.barLeft + "px";
5229 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5230 this.horiz.firstChild.style.width =
5231 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5232 } else {
5233 this.horiz.style.display = "";
5234 this.horiz.firstChild.style.width = "0";
5235 }
5236
5237 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5238 if (sWidth == 0) { this.zeroWidthHack(); }
5239 this.checkedZeroWidth = true;
5240 }
5241
5242 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5243 };
5244
5245 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5246 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5247 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5248 };
5249
5250 NativeScrollbars.prototype.setScrollTop = function (pos) {
5251 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5252 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5253 };
5254
5255 NativeScrollbars.prototype.zeroWidthHack = function () {
5256 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5257 this.horiz.style.height = this.vert.style.width = w;
5258 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5259 this.disableHoriz = new Delayed;
5260 this.disableVert = new Delayed;
5261 };
5262
5263 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5264 bar.style.pointerEvents = "auto";
5265 function maybeDisable() {
5266 // To find out whether the scrollbar is still visible, we
5267 // check whether the element under the pixel in the bottom
5268 // right corner of the scrollbar box is the scrollbar box
5269 // itself (when the bar is still visible) or its filler child
5270 // (when the bar is hidden). If it is still visible, we keep
5271 // it enabled, if it's hidden, we disable pointer events.
5272 var box = bar.getBoundingClientRect();
5273 var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5274 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5275 if (elt != bar) { bar.style.pointerEvents = "none"; }
5276 else { delay.set(1000, maybeDisable); }
5277 }
5278 delay.set(1000, maybeDisable);
5279 };
5280
5281 NativeScrollbars.prototype.clear = function () {
5282 var parent = this.horiz.parentNode;
5283 parent.removeChild(this.horiz);
5284 parent.removeChild(this.vert);
5285 };
5286
5287 var NullScrollbars = function () {};
5288
5289 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5290 NullScrollbars.prototype.setScrollLeft = function () {};
5291 NullScrollbars.prototype.setScrollTop = function () {};
5292 NullScrollbars.prototype.clear = function () {};
5293
5294 function updateScrollbars(cm, measure) {
5295 if (!measure) { measure = measureForScrollbars(cm); }
5296 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5297 updateScrollbarsInner(cm, measure);
5298 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5299 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5300 { updateHeightsInViewport(cm); }
5301 updateScrollbarsInner(cm, measureForScrollbars(cm));
5302 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5303 }
5304 }
5305
5306 // Re-synchronize the fake scrollbars with the actual size of the
5307 // content.
5308 function updateScrollbarsInner(cm, measure) {
5309 var d = cm.display;
5310 var sizes = d.scrollbars.update(measure);
5311
5312 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5313 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5314 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5315
5316 if (sizes.right && sizes.bottom) {
5317 d.scrollbarFiller.style.display = "block";
5318 d.scrollbarFiller.style.height = sizes.bottom + "px";
5319 d.scrollbarFiller.style.width = sizes.right + "px";
5320 } else { d.scrollbarFiller.style.display = ""; }
5321 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5322 d.gutterFiller.style.display = "block";
5323 d.gutterFiller.style.height = sizes.bottom + "px";
5324 d.gutterFiller.style.width = measure.gutterWidth + "px";
5325 } else { d.gutterFiller.style.display = ""; }
5326 }
5327
5328 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5329
5330 function initScrollbars(cm) {
5331 if (cm.display.scrollbars) {
5332 cm.display.scrollbars.clear();
5333 if (cm.display.scrollbars.addClass)
5334 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5335 }
5336
5337 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5338 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5339 // Prevent clicks in the scrollbars from killing focus
5340 on(node, "mousedown", function () {
5341 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5342 });
5343 node.setAttribute("cm-not-content", "true");
5344 }, function (pos, axis) {
5345 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5346 else { updateScrollTop(cm, pos); }
5347 }, cm);
5348 if (cm.display.scrollbars.addClass)
5349 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5350 }
5351
5352 // Operations are used to wrap a series of changes to the editor
5353 // state in such a way that each change won't have to update the
5354 // cursor and display (which would be awkward, slow, and
5355 // error-prone). Instead, display updates are batched and then all
5356 // combined and executed at once.
5357
5358 var nextOpId = 0;
5359 // Start a new operation.
5360 function startOperation(cm) {
5361 cm.curOp = {
5362 cm: cm,
5363 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5364 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5365 forceUpdate: false, // Used to force a redraw
5366 updateInput: 0, // Whether to reset the input textarea
5367 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5368 changeObjs: null, // Accumulated changes, for firing change events
5369 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5370 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5371 selectionChanged: false, // Whether the selection needs to be redrawn
5372 updateMaxLine: false, // Set when the widest line needs to be determined anew
5373 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5374 scrollToPos: null, // Used to scroll to a specific position
5375 focus: false,
5376 id: ++nextOpId // Unique ID
5377 };
5378 pushOperation(cm.curOp);
5379 }
5380
5381 // Finish an operation, updating the display and signalling delayed events
5382 function endOperation(cm) {
5383 var op = cm.curOp;
5384 if (op) { finishOperation(op, function (group) {
5385 for (var i = 0; i < group.ops.length; i++)
5386 { group.ops[i].cm.curOp = null; }
5387 endOperations(group);
5388 }); }
5389 }
5390
5391 // The DOM updates done when an operation finishes are batched so
5392 // that the minimum number of relayouts are required.
5393 function endOperations(group) {
5394 var ops = group.ops;
5395 for (var i = 0; i < ops.length; i++) // Read DOM
5396 { endOperation_R1(ops[i]); }
5397 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5398 { endOperation_W1(ops[i$1]); }
5399 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5400 { endOperation_R2(ops[i$2]); }
5401 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5402 { endOperation_W2(ops[i$3]); }
5403 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5404 { endOperation_finish(ops[i$4]); }
5405 }
5406
5407 function endOperation_R1(op) {
5408 var cm = op.cm, display = cm.display;
5409 maybeClipScrollbars(cm);
5410 if (op.updateMaxLine) { findMaxLine(cm); }
5411
5412 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5413 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5414 op.scrollToPos.to.line >= display.viewTo) ||
5415 display.maxLineChanged && cm.options.lineWrapping;
5416 op.update = op.mustUpdate &&
5417 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5418 }
5419
5420 function endOperation_W1(op) {
5421 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5422 }
5423
5424 function endOperation_R2(op) {
5425 var cm = op.cm, display = cm.display;
5426 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5427
5428 op.barMeasure = measureForScrollbars(cm);
5429
5430 // If the max line changed since it was last measured, measure it,
5431 // and ensure the document's width matches it.
5432 // updateDisplay_W2 will use these properties to do the actual resizing
5433 if (display.maxLineChanged && !cm.options.lineWrapping) {
5434 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5435 cm.display.sizerWidth = op.adjustWidthTo;
5436 op.barMeasure.scrollWidth =
5437 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5438 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5439 }
5440
5441 if (op.updatedDisplay || op.selectionChanged)
5442 { op.preparedSelection = display.input.prepareSelection(); }
5443 }
5444
5445 function endOperation_W2(op) {
5446 var cm = op.cm;
5447
5448 if (op.adjustWidthTo != null) {
5449 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5450 if (op.maxScrollLeft < cm.doc.scrollLeft)
5451 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5452 cm.display.maxLineChanged = false;
5453 }
5454
5455 var takeFocus = op.focus && op.focus == activeElt();
5456 if (op.preparedSelection)
5457 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5458 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5459 { updateScrollbars(cm, op.barMeasure); }
5460 if (op.updatedDisplay)
5461 { setDocumentHeight(cm, op.barMeasure); }
5462
5463 if (op.selectionChanged) { restartBlink(cm); }
5464
5465 if (cm.state.focused && op.updateInput)
5466 { cm.display.input.reset(op.typing); }
5467 if (takeFocus) { ensureFocus(op.cm); }
5468 }
5469
5470 function endOperation_finish(op) {
5471 var cm = op.cm, display = cm.display, doc = cm.doc;
5472
5473 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5474
5475 // Abort mouse wheel delta measurement, when scrolling explicitly
5476 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5477 { display.wheelStartX = display.wheelStartY = null; }
5478
5479 // Propagate the scroll position to the actual DOM scroller
5480 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5481
5482 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5483 // If we need to scroll a specific position into view, do so.
5484 if (op.scrollToPos) {
5485 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5486 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5487 maybeScrollWindow(cm, rect);
5488 }
5489
5490 // Fire events for markers that are hidden/unidden by editing or
5491 // undoing
5492 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5493 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5494 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5495 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5496 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5497
5498 if (display.wrapper.offsetHeight)
5499 { doc.scrollTop = cm.display.scroller.scrollTop; }
5500
5501 // Fire change events, and delayed event handlers
5502 if (op.changeObjs)
5503 { signal(cm, "changes", cm, op.changeObjs); }
5504 if (op.update)
5505 { op.update.finish(); }
5506 }
5507
5508 // Run the given function in an operation
5509 function runInOp(cm, f) {
5510 if (cm.curOp) { return f() }
5511 startOperation(cm);
5512 try { return f() }
5513 finally { endOperation(cm); }
5514 }
5515 // Wraps a function in an operation. Returns the wrapped function.
5516 function operation(cm, f) {
5517 return function() {
5518 if (cm.curOp) { return f.apply(cm, arguments) }
5519 startOperation(cm);
5520 try { return f.apply(cm, arguments) }
5521 finally { endOperation(cm); }
5522 }
5523 }
5524 // Used to add methods to editor and doc instances, wrapping them in
5525 // operations.
5526 function methodOp(f) {
5527 return function() {
5528 if (this.curOp) { return f.apply(this, arguments) }
5529 startOperation(this);
5530 try { return f.apply(this, arguments) }
5531 finally { endOperation(this); }
5532 }
5533 }
5534 function docMethodOp(f) {
5535 return function() {
5536 var cm = this.cm;
5537 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5538 startOperation(cm);
5539 try { return f.apply(this, arguments) }
5540 finally { endOperation(cm); }
5541 }
5542 }
5543
5544 // HIGHLIGHT WORKER
5545
5546 function startWorker(cm, time) {
5547 if (cm.doc.highlightFrontier < cm.display.viewTo)
5548 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5549 }
5550
5551 function highlightWorker(cm) {
5552 var doc = cm.doc;
5553 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5554 var end = +new Date + cm.options.workTime;
5555 var context = getContextBefore(cm, doc.highlightFrontier);
5556 var changedLines = [];
5557
5558 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5559 if (context.line >= cm.display.viewFrom) { // Visible
5560 var oldStyles = line.styles;
5561 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5562 var highlighted = highlightLine(cm, line, context, true);
5563 if (resetState) { context.state = resetState; }
5564 line.styles = highlighted.styles;
5565 var oldCls = line.styleClasses, newCls = highlighted.classes;
5566 if (newCls) { line.styleClasses = newCls; }
5567 else if (oldCls) { line.styleClasses = null; }
5568 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5569 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5570 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5571 if (ischange) { changedLines.push(context.line); }
5572 line.stateAfter = context.save();
5573 context.nextLine();
5574 } else {
5575 if (line.text.length <= cm.options.maxHighlightLength)
5576 { processLine(cm, line.text, context); }
5577 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5578 context.nextLine();
5579 }
5580 if (+new Date > end) {
5581 startWorker(cm, cm.options.workDelay);
5582 return true
5583 }
5584 });
5585 doc.highlightFrontier = context.line;
5586 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5587 if (changedLines.length) { runInOp(cm, function () {
5588 for (var i = 0; i < changedLines.length; i++)
5589 { regLineChange(cm, changedLines[i], "text"); }
5590 }); }
5591 }
5592
5593 // DISPLAY DRAWING
5594
5595 var DisplayUpdate = function(cm, viewport, force) {
5596 var display = cm.display;
5597
5598 this.viewport = viewport;
5599 // Store some values that we'll need later (but don't want to force a relayout for)
5600 this.visible = visibleLines(display, cm.doc, viewport);
5601 this.editorIsHidden = !display.wrapper.offsetWidth;
5602 this.wrapperHeight = display.wrapper.clientHeight;
5603 this.wrapperWidth = display.wrapper.clientWidth;
5604 this.oldDisplayWidth = displayWidth(cm);
5605 this.force = force;
5606 this.dims = getDimensions(cm);
5607 this.events = [];
5608 };
5609
5610 DisplayUpdate.prototype.signal = function (emitter, type) {
5611 if (hasHandler(emitter, type))
5612 { this.events.push(arguments); }
5613 };
5614 DisplayUpdate.prototype.finish = function () {
5615 for (var i = 0; i < this.events.length; i++)
5616 { signal.apply(null, this.events[i]); }
5617 };
5618
5619 function maybeClipScrollbars(cm) {
5620 var display = cm.display;
5621 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5622 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5623 display.heightForcer.style.height = scrollGap(cm) + "px";
5624 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5625 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5626 display.scrollbarsClipped = true;
5627 }
5628 }
5629
5630 function selectionSnapshot(cm) {
5631 if (cm.hasFocus()) { return null }
5632 var active = activeElt();
5633 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5634 var result = {activeElt: active};
5635 if (window.getSelection) {
5636 var sel = window.getSelection();
5637 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5638 result.anchorNode = sel.anchorNode;
5639 result.anchorOffset = sel.anchorOffset;
5640 result.focusNode = sel.focusNode;
5641 result.focusOffset = sel.focusOffset;
5642 }
5643 }
5644 return result
5645 }
5646
5647 function restoreSelection(snapshot) {
5648 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5649 snapshot.activeElt.focus();
5650 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5651 var sel = window.getSelection(), range = document.createRange();
5652 range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5653 range.collapse(false);
5654 sel.removeAllRanges();
5655 sel.addRange(range);
5656 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5657 }
5658 }
5659
5660 // Does the actual updating of the line display. Bails out
5661 // (returning false) when there is nothing to be done and forced is
5662 // false.
5663 function updateDisplayIfNeeded(cm, update) {
5664 var display = cm.display, doc = cm.doc;
5665
5666 if (update.editorIsHidden) {
5667 resetView(cm);
5668 return false
5669 }
5670
5671 // Bail out if the visible area is already rendered and nothing changed.
5672 if (!update.force &&
5673 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5674 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5675 display.renderedView == display.view && countDirtyView(cm) == 0)
5676 { return false }
5677
5678 if (maybeUpdateLineNumberWidth(cm)) {
5679 resetView(cm);
5680 update.dims = getDimensions(cm);
5681 }
5682
5683 // Compute a suitable new viewport (from & to)
5684 var end = doc.first + doc.size;
5685 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5686 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5687 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5688 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5689 if (sawCollapsedSpans) {
5690 from = visualLineNo(cm.doc, from);
5691 to = visualLineEndNo(cm.doc, to);
5692 }
5693
5694 var different = from != display.viewFrom || to != display.viewTo ||
5695 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5696 adjustView(cm, from, to);
5697
5698 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5699 // Position the mover div to align with the current scroll position
5700 cm.display.mover.style.top = display.viewOffset + "px";
5701
5702 var toUpdate = countDirtyView(cm);
5703 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5704 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5705 { return false }
5706
5707 // For big changes, we hide the enclosing element during the
5708 // update, since that speeds up the operations on most browsers.
5709 var selSnapshot = selectionSnapshot(cm);
5710 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5711 patchDisplay(cm, display.updateLineNumbers, update.dims);
5712 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5713 display.renderedView = display.view;
5714 // There might have been a widget with a focused element that got
5715 // hidden or updated, if so re-focus it.
5716 restoreSelection(selSnapshot);
5717
5718 // Prevent selection and cursors from interfering with the scroll
5719 // width and height.
5720 removeChildren(display.cursorDiv);
5721 removeChildren(display.selectionDiv);
5722 display.gutters.style.height = display.sizer.style.minHeight = 0;
5723
5724 if (different) {
5725 display.lastWrapHeight = update.wrapperHeight;
5726 display.lastWrapWidth = update.wrapperWidth;
5727 startWorker(cm, 400);
5728 }
5729
5730 display.updateLineNumbers = null;
5731
5732 return true
5733 }
5734
5735 function postUpdateDisplay(cm, update) {
5736 var viewport = update.viewport;
5737
5738 for (var first = true;; first = false) {
5739 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5740 // Clip forced viewport to actual scrollable area.
5741 if (viewport && viewport.top != null)
5742 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5743 // Updated line heights might result in the drawn area not
5744 // actually covering the viewport. Keep looping until it does.
5745 update.visible = visibleLines(cm.display, cm.doc, viewport);
5746 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5747 { break }
5748 } else if (first) {
5749 update.visible = visibleLines(cm.display, cm.doc, viewport);
5750 }
5751 if (!updateDisplayIfNeeded(cm, update)) { break }
5752 updateHeightsInViewport(cm);
5753 var barMeasure = measureForScrollbars(cm);
5754 updateSelection(cm);
5755 updateScrollbars(cm, barMeasure);
5756 setDocumentHeight(cm, barMeasure);
5757 update.force = false;
5758 }
5759
5760 update.signal(cm, "update", cm);
5761 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5762 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5763 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5764 }
5765 }
5766
5767 function updateDisplaySimple(cm, viewport) {
5768 var update = new DisplayUpdate(cm, viewport);
5769 if (updateDisplayIfNeeded(cm, update)) {
5770 updateHeightsInViewport(cm);
5771 postUpdateDisplay(cm, update);
5772 var barMeasure = measureForScrollbars(cm);
5773 updateSelection(cm);
5774 updateScrollbars(cm, barMeasure);
5775 setDocumentHeight(cm, barMeasure);
5776 update.finish();
5777 }
5778 }
5779
5780 // Sync the actual display DOM structure with display.view, removing
5781 // nodes for lines that are no longer in view, and creating the ones
5782 // that are not there yet, and updating the ones that are out of
5783 // date.
5784 function patchDisplay(cm, updateNumbersFrom, dims) {
5785 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5786 var container = display.lineDiv, cur = container.firstChild;
5787
5788 function rm(node) {
5789 var next = node.nextSibling;
5790 // Works around a throw-scroll bug in OS X Webkit
5791 if (webkit && mac && cm.display.currentWheelTarget == node)
5792 { node.style.display = "none"; }
5793 else
5794 { node.parentNode.removeChild(node); }
5795 return next
5796 }
5797
5798 var view = display.view, lineN = display.viewFrom;
5799 // Loop over the elements in the view, syncing cur (the DOM nodes
5800 // in display.lineDiv) with the view as we go.
5801 for (var i = 0; i < view.length; i++) {
5802 var lineView = view[i];
5803 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5804 var node = buildLineElement(cm, lineView, lineN, dims);
5805 container.insertBefore(node, cur);
5806 } else { // Already drawn
5807 while (cur != lineView.node) { cur = rm(cur); }
5808 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5809 updateNumbersFrom <= lineN && lineView.lineNumber;
5810 if (lineView.changes) {
5811 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5812 updateLineForChanges(cm, lineView, lineN, dims);
5813 }
5814 if (updateNumber) {
5815 removeChildren(lineView.lineNumber);
5816 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5817 }
5818 cur = lineView.node.nextSibling;
5819 }
5820 lineN += lineView.size;
5821 }
5822 while (cur) { cur = rm(cur); }
5823 }
5824
5825 function updateGutterSpace(display) {
5826 var width = display.gutters.offsetWidth;
5827 display.sizer.style.marginLeft = width + "px";
5828 }
5829
5830 function setDocumentHeight(cm, measure) {
5831 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5832 cm.display.heightForcer.style.top = measure.docHeight + "px";
5833 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5834 }
5835
5836 // Re-align line numbers and gutter marks to compensate for
5837 // horizontal scrolling.
5838 function alignHorizontally(cm) {
5839 var display = cm.display, view = display.view;
5840 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
5841 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
5842 var gutterW = display.gutters.offsetWidth, left = comp + "px";
5843 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
5844 if (cm.options.fixedGutter) {
5845 if (view[i].gutter)
5846 { view[i].gutter.style.left = left; }
5847 if (view[i].gutterBackground)
5848 { view[i].gutterBackground.style.left = left; }
5849 }
5850 var align = view[i].alignable;
5851 if (align) { for (var j = 0; j < align.length; j++)
5852 { align[j].style.left = left; } }
5853 } }
5854 if (cm.options.fixedGutter)
5855 { display.gutters.style.left = (comp + gutterW) + "px"; }
5856 }
5857
5858 // Used to ensure that the line number gutter is still the right
5859 // size for the current document size. Returns true when an update
5860 // is needed.
5861 function maybeUpdateLineNumberWidth(cm) {
5862 if (!cm.options.lineNumbers) { return false }
5863 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
5864 if (last.length != display.lineNumChars) {
5865 var test = display.measure.appendChild(elt("div", [elt("div", last)],
5866 "CodeMirror-linenumber CodeMirror-gutter-elt"));
5867 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
5868 display.lineGutter.style.width = "";
5869 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
5870 display.lineNumWidth = display.lineNumInnerWidth + padding;
5871 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
5872 display.lineGutter.style.width = display.lineNumWidth + "px";
5873 updateGutterSpace(cm.display);
5874 return true
5875 }
5876 return false
5877 }
5878
5879 function getGutters(gutters, lineNumbers) {
5880 var result = [], sawLineNumbers = false;
5881 for (var i = 0; i < gutters.length; i++) {
5882 var name = gutters[i], style = null;
5883 if (typeof name != "string") { style = name.style; name = name.className; }
5884 if (name == "CodeMirror-linenumbers") {
5885 if (!lineNumbers) { continue }
5886 else { sawLineNumbers = true; }
5887 }
5888 result.push({className: name, style: style});
5889 }
5890 if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
5891 return result
5892 }
5893
5894 // Rebuild the gutter elements, ensure the margin to the left of the
5895 // code matches their width.
5896 function renderGutters(display) {
5897 var gutters = display.gutters, specs = display.gutterSpecs;
5898 removeChildren(gutters);
5899 display.lineGutter = null;
5900 for (var i = 0; i < specs.length; ++i) {
5901 var ref = specs[i];
5902 var className = ref.className;
5903 var style = ref.style;
5904 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
5905 if (style) { gElt.style.cssText = style; }
5906 if (className == "CodeMirror-linenumbers") {
5907 display.lineGutter = gElt;
5908 gElt.style.width = (display.lineNumWidth || 1) + "px";
5909 }
5910 }
5911 gutters.style.display = specs.length ? "" : "none";
5912 updateGutterSpace(display);
5913 }
5914
5915 function updateGutters(cm) {
5916 renderGutters(cm.display);
5917 regChange(cm);
5918 alignHorizontally(cm);
5919 }
5920
5921 // The display handles the DOM integration, both for input reading
5922 // and content drawing. It holds references to DOM nodes and
5923 // display-related state.
5924
5925 function Display(place, doc, input, options) {
5926 var d = this;
5927 this.input = input;
5928
5929 // Covers bottom-right square when both scrollbars are present.
5930 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
5931 d.scrollbarFiller.setAttribute("cm-not-content", "true");
5932 // Covers bottom of gutter when coverGutterNextToScrollbar is on
5933 // and h scrollbar is present.
5934 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
5935 d.gutterFiller.setAttribute("cm-not-content", "true");
5936 // Will contain the actual code, positioned to cover the viewport.
5937 d.lineDiv = eltP("div", null, "CodeMirror-code");
5938 // Elements are added to these to represent selection and cursors.
5939 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
5940 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
5941 // A visibility: hidden element used to find the size of things.
5942 d.measure = elt("div", null, "CodeMirror-measure");
5943 // When lines outside of the viewport are measured, they are drawn in this.
5944 d.lineMeasure = elt("div", null, "CodeMirror-measure");
5945 // Wraps everything that needs to exist inside the vertically-padded coordinate system
5946 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
5947 null, "position: relative; outline: none");
5948 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
5949 // Moved around its parent to cover visible view.
5950 d.mover = elt("div", [lines], null, "position: relative");
5951 // Set to the height of the document, allowing scrolling.
5952 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
5953 d.sizerWidth = null;
5954 // Behavior of elts with overflow: auto and padding is
5955 // inconsistent across browsers. This is used to ensure the
5956 // scrollable area is big enough.
5957 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
5958 // Will contain the gutters, if any.
5959 d.gutters = elt("div", null, "CodeMirror-gutters");
5960 d.lineGutter = null;
5961 // Actual scrollable element.
5962 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
5963 d.scroller.setAttribute("tabIndex", "-1");
5964 // The element in which the editor lives.
5965 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
5966
5967 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
5968 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
5969 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
5970
5971 if (place) {
5972 if (place.appendChild) { place.appendChild(d.wrapper); }
5973 else { place(d.wrapper); }
5974 }
5975
5976 // Current rendered range (may be bigger than the view window).
5977 d.viewFrom = d.viewTo = doc.first;
5978 d.reportedViewFrom = d.reportedViewTo = doc.first;
5979 // Information about the rendered lines.
5980 d.view = [];
5981 d.renderedView = null;
5982 // Holds info about a single rendered line when it was rendered
5983 // for measurement, while not in view.
5984 d.externalMeasured = null;
5985 // Empty space (in pixels) above the view
5986 d.viewOffset = 0;
5987 d.lastWrapHeight = d.lastWrapWidth = 0;
5988 d.updateLineNumbers = null;
5989
5990 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
5991 d.scrollbarsClipped = false;
5992
5993 // Used to only resize the line number gutter when necessary (when
5994 // the amount of lines crosses a boundary that makes its width change)
5995 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
5996 // Set to true when a non-horizontal-scrolling line widget is
5997 // added. As an optimization, line widget aligning is skipped when
5998 // this is false.
5999 d.alignWidgets = false;
6000
6001 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
6002
6003 // Tracks the maximum line length so that the horizontal scrollbar
6004 // can be kept static when scrolling.
6005 d.maxLine = null;
6006 d.maxLineLength = 0;
6007 d.maxLineChanged = false;
6008
6009 // Used for measuring wheel scrolling granularity
6010 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
6011
6012 // True when shift is held down.
6013 d.shift = false;
6014
6015 // Used to track whether anything happened since the context menu
6016 // was opened.
6017 d.selForContextMenu = null;
6018
6019 d.activeTouch = null;
6020
6021 d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
6022 renderGutters(d);
6023
6024 input.init(d);
6025 }
6026
6027 // Since the delta values reported on mouse wheel events are
6028 // unstandardized between browsers and even browser versions, and
6029 // generally horribly unpredictable, this code starts by measuring
6030 // the scroll effect that the first few mouse wheel events have,
6031 // and, from that, detects the way it can convert deltas to pixel
6032 // offsets afterwards.
6033 //
6034 // The reason we want to know the amount a wheel event will scroll
6035 // is that it gives us a chance to update the display before the
6036 // actual scrolling happens, reducing flickering.
6037
6038 var wheelSamples = 0, wheelPixelsPerUnit = null;
6039 // Fill in a browser-detected starting value on browsers where we
6040 // know one. These don't have to be accurate -- the result of them
6041 // being wrong would just be a slight flicker on the first wheel
6042 // scroll (if it is large enough).
6043 if (ie) { wheelPixelsPerUnit = -.53; }
6044 else if (gecko) { wheelPixelsPerUnit = 15; }
6045 else if (chrome) { wheelPixelsPerUnit = -.7; }
6046 else if (safari) { wheelPixelsPerUnit = -1/3; }
6047
6048 function wheelEventDelta(e) {
6049 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
6050 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
6051 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
6052 else if (dy == null) { dy = e.wheelDelta; }
6053 return {x: dx, y: dy}
6054 }
6055 function wheelEventPixels(e) {
6056 var delta = wheelEventDelta(e);
6057 delta.x *= wheelPixelsPerUnit;
6058 delta.y *= wheelPixelsPerUnit;
6059 return delta
6060 }
6061
6062 function onScrollWheel(cm, e) {
6063 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
6064
6065 var display = cm.display, scroll = display.scroller;
6066 // Quit if there's nothing to scroll here
6067 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
6068 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
6069 if (!(dx && canScrollX || dy && canScrollY)) { return }
6070
6071 // Webkit browsers on OS X abort momentum scrolls when the target
6072 // of the scroll event is removed from the scrollable element.
6073 // This hack (see related code in patchDisplay) makes sure the
6074 // element is kept around.
6075 if (dy && mac && webkit) {
6076 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
6077 for (var i = 0; i < view.length; i++) {
6078 if (view[i].node == cur) {
6079 cm.display.currentWheelTarget = cur;
6080 break outer
6081 }
6082 }
6083 }
6084 }
6085
6086 // On some browsers, horizontal scrolling will cause redraws to
6087 // happen before the gutter has been realigned, causing it to
6088 // wriggle around in a most unseemly way. When we have an
6089 // estimated pixels/delta value, we just handle horizontal
6090 // scrolling entirely here. It'll be slightly off from native, but
6091 // better than glitching out.
6092 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6093 if (dy && canScrollY)
6094 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
6095 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
6096 // Only prevent default scrolling if vertical scrolling is
6097 // actually possible. Otherwise, it causes vertical scroll
6098 // jitter on OSX trackpads when deltaX is small and deltaY
6099 // is large (issue #3579)
6100 if (!dy || (dy && canScrollY))
6101 { e_preventDefault(e); }
6102 display.wheelStartX = null; // Abort measurement, if in progress
6103 return
6104 }
6105
6106 // 'Project' the visible viewport to cover the area that is being
6107 // scrolled into view (if we know enough to estimate it).
6108 if (dy && wheelPixelsPerUnit != null) {
6109 var pixels = dy * wheelPixelsPerUnit;
6110 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6111 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
6112 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
6113 updateDisplaySimple(cm, {top: top, bottom: bot});
6114 }
6115
6116 if (wheelSamples < 20) {
6117 if (display.wheelStartX == null) {
6118 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6119 display.wheelDX = dx; display.wheelDY = dy;
6120 setTimeout(function () {
6121 if (display.wheelStartX == null) { return }
6122 var movedX = scroll.scrollLeft - display.wheelStartX;
6123 var movedY = scroll.scrollTop - display.wheelStartY;
6124 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6125 (movedX && display.wheelDX && movedX / display.wheelDX);
6126 display.wheelStartX = display.wheelStartY = null;
6127 if (!sample) { return }
6128 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6129 ++wheelSamples;
6130 }, 200);
6131 } else {
6132 display.wheelDX += dx; display.wheelDY += dy;
6133 }
6134 }
6135 }
6136
6137 // Selection objects are immutable. A new one is created every time
6138 // the selection changes. A selection is one or more non-overlapping
6139 // (and non-touching) ranges, sorted, and an integer that indicates
6140 // which one is the primary selection (the one that's scrolled into
6141 // view, that getCursor returns, etc).
6142 var Selection = function(ranges, primIndex) {
6143 this.ranges = ranges;
6144 this.primIndex = primIndex;
6145 };
6146
6147 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
6148
6149 Selection.prototype.equals = function (other) {
6150 if (other == this) { return true }
6151 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
6152 for (var i = 0; i < this.ranges.length; i++) {
6153 var here = this.ranges[i], there = other.ranges[i];
6154 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
6155 }
6156 return true
6157 };
6158
6159 Selection.prototype.deepCopy = function () {
6160 var out = [];
6161 for (var i = 0; i < this.ranges.length; i++)
6162 { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
6163 return new Selection(out, this.primIndex)
6164 };
6165
6166 Selection.prototype.somethingSelected = function () {
6167 for (var i = 0; i < this.ranges.length; i++)
6168 { if (!this.ranges[i].empty()) { return true } }
6169 return false
6170 };
6171
6172 Selection.prototype.contains = function (pos, end) {
6173 if (!end) { end = pos; }
6174 for (var i = 0; i < this.ranges.length; i++) {
6175 var range = this.ranges[i];
6176 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
6177 { return i }
6178 }
6179 return -1
6180 };
6181
6182 var Range = function(anchor, head) {
6183 this.anchor = anchor; this.head = head;
6184 };
6185
6186 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6187 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6188 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6189
6190 // Take an unsorted, potentially overlapping set of ranges, and
6191 // build a selection out of it. 'Consumes' ranges array (modifying
6192 // it).
6193 function normalizeSelection(cm, ranges, primIndex) {
6194 var mayTouch = cm && cm.options.selectionsMayTouch;
6195 var prim = ranges[primIndex];
6196 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6197 primIndex = indexOf(ranges, prim);
6198 for (var i = 1; i < ranges.length; i++) {
6199 var cur = ranges[i], prev = ranges[i - 1];
6200 var diff = cmp(prev.to(), cur.from());
6201 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
6202 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6203 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6204 if (i <= primIndex) { --primIndex; }
6205 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6206 }
6207 }
6208 return new Selection(ranges, primIndex)
6209 }
6210
6211 function simpleSelection(anchor, head) {
6212 return new Selection([new Range(anchor, head || anchor)], 0)
6213 }
6214
6215 // Compute the position of the end of a change (its 'to' property
6216 // refers to the pre-change end).
6217 function changeEnd(change) {
6218 if (!change.text) { return change.to }
6219 return Pos(change.from.line + change.text.length - 1,
6220 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6221 }
6222
6223 // Adjust a position to refer to the post-change position of the
6224 // same text, or the end of the change if the change covers it.
6225 function adjustForChange(pos, change) {
6226 if (cmp(pos, change.from) < 0) { return pos }
6227 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6228
6229 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6230 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6231 return Pos(line, ch)
6232 }
6233
6234 function computeSelAfterChange(doc, change) {
6235 var out = [];
6236 for (var i = 0; i < doc.sel.ranges.length; i++) {
6237 var range = doc.sel.ranges[i];
6238 out.push(new Range(adjustForChange(range.anchor, change),
6239 adjustForChange(range.head, change)));
6240 }
6241 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
6242 }
6243
6244 function offsetPos(pos, old, nw) {
6245 if (pos.line == old.line)
6246 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6247 else
6248 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6249 }
6250
6251 // Used by replaceSelections to allow moving the selection to the
6252 // start or around the replaced test. Hint may be "start" or "around".
6253 function computeReplacedSel(doc, changes, hint) {
6254 var out = [];
6255 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6256 for (var i = 0; i < changes.length; i++) {
6257 var change = changes[i];
6258 var from = offsetPos(change.from, oldPrev, newPrev);
6259 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6260 oldPrev = change.to;
6261 newPrev = to;
6262 if (hint == "around") {
6263 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6264 out[i] = new Range(inv ? to : from, inv ? from : to);
6265 } else {
6266 out[i] = new Range(from, from);
6267 }
6268 }
6269 return new Selection(out, doc.sel.primIndex)
6270 }
6271
6272 // Used to get the editor into a consistent state again when options change.
6273
6274 function loadMode(cm) {
6275 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6276 resetModeState(cm);
6277 }
6278
6279 function resetModeState(cm) {
6280 cm.doc.iter(function (line) {
6281 if (line.stateAfter) { line.stateAfter = null; }
6282 if (line.styles) { line.styles = null; }
6283 });
6284 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6285 startWorker(cm, 100);
6286 cm.state.modeGen++;
6287 if (cm.curOp) { regChange(cm); }
6288 }
6289
6290 // DOCUMENT DATA STRUCTURE
6291
6292 // By default, updates that start and end at the beginning of a line
6293 // are treated specially, in order to make the association of line
6294 // widgets and marker elements with the text behave more intuitive.
6295 function isWholeLineUpdate(doc, change) {
6296 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6297 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6298 }
6299
6300 // Perform a change on the document data structure.
6301 function updateDoc(doc, change, markedSpans, estimateHeight) {
6302 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6303 function update(line, text, spans) {
6304 updateLine(line, text, spans, estimateHeight);
6305 signalLater(line, "change", line, change);
6306 }
6307 function linesFor(start, end) {
6308 var result = [];
6309 for (var i = start; i < end; ++i)
6310 { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
6311 return result
6312 }
6313
6314 var from = change.from, to = change.to, text = change.text;
6315 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6316 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6317
6318 // Adjust the line structure
6319 if (change.full) {
6320 doc.insert(0, linesFor(0, text.length));
6321 doc.remove(text.length, doc.size - text.length);
6322 } else if (isWholeLineUpdate(doc, change)) {
6323 // This is a whole-line replace. Treated specially to make
6324 // sure line objects move the way they are supposed to.
6325 var added = linesFor(0, text.length - 1);
6326 update(lastLine, lastLine.text, lastSpans);
6327 if (nlines) { doc.remove(from.line, nlines); }
6328 if (added.length) { doc.insert(from.line, added); }
6329 } else if (firstLine == lastLine) {
6330 if (text.length == 1) {
6331 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6332 } else {
6333 var added$1 = linesFor(1, text.length - 1);
6334 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
6335 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6336 doc.insert(from.line + 1, added$1);
6337 }
6338 } else if (text.length == 1) {
6339 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6340 doc.remove(from.line + 1, nlines);
6341 } else {
6342 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6343 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6344 var added$2 = linesFor(1, text.length - 1);
6345 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6346 doc.insert(from.line + 1, added$2);
6347 }
6348
6349 signalLater(doc, "change", doc, change);
6350 }
6351
6352 // Call f for all linked documents.
6353 function linkedDocs(doc, f, sharedHistOnly) {
6354 function propagate(doc, skip, sharedHist) {
6355 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6356 var rel = doc.linked[i];
6357 if (rel.doc == skip) { continue }
6358 var shared = sharedHist && rel.sharedHist;
6359 if (sharedHistOnly && !shared) { continue }
6360 f(rel.doc, shared);
6361 propagate(rel.doc, doc, shared);
6362 } }
6363 }
6364 propagate(doc, null, true);
6365 }
6366
6367 // Attach a document to an editor.
6368 function attachDoc(cm, doc) {
6369 if (doc.cm) { throw new Error("This document is already in use.") }
6370 cm.doc = doc;
6371 doc.cm = cm;
6372 estimateLineHeights(cm);
6373 loadMode(cm);
6374 setDirectionClass(cm);
6375 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6376 cm.options.mode = doc.modeOption;
6377 regChange(cm);
6378 }
6379
6380 function setDirectionClass(cm) {
6381 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6382 }
6383
6384 function directionChanged(cm) {
6385 runInOp(cm, function () {
6386 setDirectionClass(cm);
6387 regChange(cm);
6388 });
6389 }
6390
6391 function History(startGen) {
6392 // Arrays of change events and selections. Doing something adds an
6393 // event to done and clears undo. Undoing moves events from done
6394 // to undone, redoing moves them in the other direction.
6395 this.done = []; this.undone = [];
6396 this.undoDepth = Infinity;
6397 // Used to track when changes can be merged into a single undo
6398 // event
6399 this.lastModTime = this.lastSelTime = 0;
6400 this.lastOp = this.lastSelOp = null;
6401 this.lastOrigin = this.lastSelOrigin = null;
6402 // Used by the isClean() method
6403 this.generation = this.maxGeneration = startGen || 1;
6404 }
6405
6406 // Create a history change event from an updateDoc-style change
6407 // object.
6408 function historyChangeFromChange(doc, change) {
6409 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6410 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6411 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6412 return histChange
6413 }
6414
6415 // Pop all selection events off the end of a history array. Stop at
6416 // a change event.
6417 function clearSelectionEvents(array) {
6418 while (array.length) {
6419 var last = lst(array);
6420 if (last.ranges) { array.pop(); }
6421 else { break }
6422 }
6423 }
6424
6425 // Find the top change event in the history. Pop off selection
6426 // events that are in the way.
6427 function lastChangeEvent(hist, force) {
6428 if (force) {
6429 clearSelectionEvents(hist.done);
6430 return lst(hist.done)
6431 } else if (hist.done.length && !lst(hist.done).ranges) {
6432 return lst(hist.done)
6433 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6434 hist.done.pop();
6435 return lst(hist.done)
6436 }
6437 }
6438
6439 // Register a change in the history. Merges changes that are within
6440 // a single operation, or are close together with an origin that
6441 // allows merging (starting with "+") into a single event.
6442 function addChangeToHistory(doc, change, selAfter, opId) {
6443 var hist = doc.history;
6444 hist.undone.length = 0;
6445 var time = +new Date, cur;
6446 var last;
6447
6448 if ((hist.lastOp == opId ||
6449 hist.lastOrigin == change.origin && change.origin &&
6450 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
6451 change.origin.charAt(0) == "*")) &&
6452 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6453 // Merge this change into the last event
6454 last = lst(cur.changes);
6455 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6456 // Optimized case for simple insertion -- don't want to add
6457 // new changesets for every character typed
6458 last.to = changeEnd(change);
6459 } else {
6460 // Add new sub-event
6461 cur.changes.push(historyChangeFromChange(doc, change));
6462 }
6463 } else {
6464 // Can not be merged, start a new event.
6465 var before = lst(hist.done);
6466 if (!before || !before.ranges)
6467 { pushSelectionToHistory(doc.sel, hist.done); }
6468 cur = {changes: [historyChangeFromChange(doc, change)],
6469 generation: hist.generation};
6470 hist.done.push(cur);
6471 while (hist.done.length > hist.undoDepth) {
6472 hist.done.shift();
6473 if (!hist.done[0].ranges) { hist.done.shift(); }
6474 }
6475 }
6476 hist.done.push(selAfter);
6477 hist.generation = ++hist.maxGeneration;
6478 hist.lastModTime = hist.lastSelTime = time;
6479 hist.lastOp = hist.lastSelOp = opId;
6480 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6481
6482 if (!last) { signal(doc, "historyAdded"); }
6483 }
6484
6485 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6486 var ch = origin.charAt(0);
6487 return ch == "*" ||
6488 ch == "+" &&
6489 prev.ranges.length == sel.ranges.length &&
6490 prev.somethingSelected() == sel.somethingSelected() &&
6491 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6492 }
6493
6494 // Called whenever the selection changes, sets the new selection as
6495 // the pending selection in the history, and pushes the old pending
6496 // selection into the 'done' array when it was significantly
6497 // different (in number of selected ranges, emptiness, or time).
6498 function addSelectionToHistory(doc, sel, opId, options) {
6499 var hist = doc.history, origin = options && options.origin;
6500
6501 // A new event is started when the previous origin does not match
6502 // the current, or the origins don't allow matching. Origins
6503 // starting with * are always merged, those starting with + are
6504 // merged when similar and close together in time.
6505 if (opId == hist.lastSelOp ||
6506 (origin && hist.lastSelOrigin == origin &&
6507 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6508 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6509 { hist.done[hist.done.length - 1] = sel; }
6510 else
6511 { pushSelectionToHistory(sel, hist.done); }
6512
6513 hist.lastSelTime = +new Date;
6514 hist.lastSelOrigin = origin;
6515 hist.lastSelOp = opId;
6516 if (options && options.clearRedo !== false)
6517 { clearSelectionEvents(hist.undone); }
6518 }
6519
6520 function pushSelectionToHistory(sel, dest) {
6521 var top = lst(dest);
6522 if (!(top && top.ranges && top.equals(sel)))
6523 { dest.push(sel); }
6524 }
6525
6526 // Used to store marked span information in the history.
6527 function attachLocalSpans(doc, change, from, to) {
6528 var existing = change["spans_" + doc.id], n = 0;
6529 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6530 if (line.markedSpans)
6531 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6532 ++n;
6533 });
6534 }
6535
6536 // When un/re-doing restores text containing marked spans, those
6537 // that have been explicitly cleared should not be restored.
6538 function removeClearedSpans(spans) {
6539 if (!spans) { return null }
6540 var out;
6541 for (var i = 0; i < spans.length; ++i) {
6542 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6543 else if (out) { out.push(spans[i]); }
6544 }
6545 return !out ? spans : out.length ? out : null
6546 }
6547
6548 // Retrieve and filter the old marked spans stored in a change event.
6549 function getOldSpans(doc, change) {
6550 var found = change["spans_" + doc.id];
6551 if (!found) { return null }
6552 var nw = [];
6553 for (var i = 0; i < change.text.length; ++i)
6554 { nw.push(removeClearedSpans(found[i])); }
6555 return nw
6556 }
6557
6558 // Used for un/re-doing changes from the history. Combines the
6559 // result of computing the existing spans with the set of spans that
6560 // existed in the history (so that deleting around a span and then
6561 // undoing brings back the span).
6562 function mergeOldSpans(doc, change) {
6563 var old = getOldSpans(doc, change);
6564 var stretched = stretchSpansOverChange(doc, change);
6565 if (!old) { return stretched }
6566 if (!stretched) { return old }
6567
6568 for (var i = 0; i < old.length; ++i) {
6569 var oldCur = old[i], stretchCur = stretched[i];
6570 if (oldCur && stretchCur) {
6571 spans: for (var j = 0; j < stretchCur.length; ++j) {
6572 var span = stretchCur[j];
6573 for (var k = 0; k < oldCur.length; ++k)
6574 { if (oldCur[k].marker == span.marker) { continue spans } }
6575 oldCur.push(span);
6576 }
6577 } else if (stretchCur) {
6578 old[i] = stretchCur;
6579 }
6580 }
6581 return old
6582 }
6583
6584 // Used both to provide a JSON-safe object in .getHistory, and, when
6585 // detaching a document, to split the history in two
6586 function copyHistoryArray(events, newGroup, instantiateSel) {
6587 var copy = [];
6588 for (var i = 0; i < events.length; ++i) {
6589 var event = events[i];
6590 if (event.ranges) {
6591 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6592 continue
6593 }
6594 var changes = event.changes, newChanges = [];
6595 copy.push({changes: newChanges});
6596 for (var j = 0; j < changes.length; ++j) {
6597 var change = changes[j], m = (void 0);
6598 newChanges.push({from: change.from, to: change.to, text: change.text});
6599 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6600 if (indexOf(newGroup, Number(m[1])) > -1) {
6601 lst(newChanges)[prop] = change[prop];
6602 delete change[prop];
6603 }
6604 } } }
6605 }
6606 }
6607 return copy
6608 }
6609
6610 // The 'scroll' parameter given to many of these indicated whether
6611 // the new cursor position should be scrolled into view after
6612 // modifying the selection.
6613
6614 // If shift is held or the extend flag is set, extends a range to
6615 // include a given position (and optionally a second position).
6616 // Otherwise, simply returns the range between the given positions.
6617 // Used for cursor motion and such.
6618 function extendRange(range, head, other, extend) {
6619 if (extend) {
6620 var anchor = range.anchor;
6621 if (other) {
6622 var posBefore = cmp(head, anchor) < 0;
6623 if (posBefore != (cmp(other, anchor) < 0)) {
6624 anchor = head;
6625 head = other;
6626 } else if (posBefore != (cmp(head, other) < 0)) {
6627 head = other;
6628 }
6629 }
6630 return new Range(anchor, head)
6631 } else {
6632 return new Range(other || head, head)
6633 }
6634 }
6635
6636 // Extend the primary selection range, discard the rest.
6637 function extendSelection(doc, head, other, options, extend) {
6638 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6639 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6640 }
6641
6642 // Extend all selections (pos is an array of selections with length
6643 // equal the number of selections)
6644 function extendSelections(doc, heads, options) {
6645 var out = [];
6646 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6647 for (var i = 0; i < doc.sel.ranges.length; i++)
6648 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6649 var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
6650 setSelection(doc, newSel, options);
6651 }
6652
6653 // Updates a single range in the selection.
6654 function replaceOneSelection(doc, i, range, options) {
6655 var ranges = doc.sel.ranges.slice(0);
6656 ranges[i] = range;
6657 setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
6658 }
6659
6660 // Reset the selection to a single range.
6661 function setSimpleSelection(doc, anchor, head, options) {
6662 setSelection(doc, simpleSelection(anchor, head), options);
6663 }
6664
6665 // Give beforeSelectionChange handlers a change to influence a
6666 // selection update.
6667 function filterSelectionChange(doc, sel, options) {
6668 var obj = {
6669 ranges: sel.ranges,
6670 update: function(ranges) {
6671 this.ranges = [];
6672 for (var i = 0; i < ranges.length; i++)
6673 { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6674 clipPos(doc, ranges[i].head)); }
6675 },
6676 origin: options && options.origin
6677 };
6678 signal(doc, "beforeSelectionChange", doc, obj);
6679 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6680 if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
6681 else { return sel }
6682 }
6683
6684 function setSelectionReplaceHistory(doc, sel, options) {
6685 var done = doc.history.done, last = lst(done);
6686 if (last && last.ranges) {
6687 done[done.length - 1] = sel;
6688 setSelectionNoUndo(doc, sel, options);
6689 } else {
6690 setSelection(doc, sel, options);
6691 }
6692 }
6693
6694 // Set a new selection.
6695 function setSelection(doc, sel, options) {
6696 setSelectionNoUndo(doc, sel, options);
6697 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6698 }
6699
6700 function setSelectionNoUndo(doc, sel, options) {
6701 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6702 { sel = filterSelectionChange(doc, sel, options); }
6703
6704 var bias = options && options.bias ||
6705 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6706 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6707
6708 if (!(options && options.scroll === false) && doc.cm)
6709 { ensureCursorVisible(doc.cm); }
6710 }
6711
6712 function setSelectionInner(doc, sel) {
6713 if (sel.equals(doc.sel)) { return }
6714
6715 doc.sel = sel;
6716
6717 if (doc.cm) {
6718 doc.cm.curOp.updateInput = 1;
6719 doc.cm.curOp.selectionChanged = true;
6720 signalCursorActivity(doc.cm);
6721 }
6722 signalLater(doc, "cursorActivity", doc);
6723 }
6724
6725 // Verify that the selection does not partially select any atomic
6726 // marked ranges.
6727 function reCheckSelection(doc) {
6728 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6729 }
6730
6731 // Return a selection that does not partially select any atomic
6732 // ranges.
6733 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6734 var out;
6735 for (var i = 0; i < sel.ranges.length; i++) {
6736 var range = sel.ranges[i];
6737 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6738 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6739 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6740 if (out || newAnchor != range.anchor || newHead != range.head) {
6741 if (!out) { out = sel.ranges.slice(0, i); }
6742 out[i] = new Range(newAnchor, newHead);
6743 }
6744 }
6745 return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
6746 }
6747
6748 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6749 var line = getLine(doc, pos.line);
6750 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6751 var sp = line.markedSpans[i], m = sp.marker;
6752
6753 // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
6754 // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
6755 // is with selectLeft/Right
6756 var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
6757 var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
6758
6759 if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6760 (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6761 if (mayClear) {
6762 signal(m, "beforeCursorEnter");
6763 if (m.explicitlyCleared) {
6764 if (!line.markedSpans) { break }
6765 else {--i; continue}
6766 }
6767 }
6768 if (!m.atomic) { continue }
6769
6770 if (oldPos) {
6771 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6772 if (dir < 0 ? preventCursorRight : preventCursorLeft)
6773 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6774 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6775 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6776 }
6777
6778 var far = m.find(dir < 0 ? -1 : 1);
6779 if (dir < 0 ? preventCursorLeft : preventCursorRight)
6780 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6781 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6782 }
6783 } }
6784 return pos
6785 }
6786
6787 // Ensure a given position is not inside an atomic range.
6788 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6789 var dir = bias || 1;
6790 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6791 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6792 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6793 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6794 if (!found) {
6795 doc.cantEdit = true;
6796 return Pos(doc.first, 0)
6797 }
6798 return found
6799 }
6800
6801 function movePos(doc, pos, dir, line) {
6802 if (dir < 0 && pos.ch == 0) {
6803 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6804 else { return null }
6805 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6806 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6807 else { return null }
6808 } else {
6809 return new Pos(pos.line, pos.ch + dir)
6810 }
6811 }
6812
6813 function selectAll(cm) {
6814 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6815 }
6816
6817 // UPDATING
6818
6819 // Allow "beforeChange" event handlers to influence a change
6820 function filterChange(doc, change, update) {
6821 var obj = {
6822 canceled: false,
6823 from: change.from,
6824 to: change.to,
6825 text: change.text,
6826 origin: change.origin,
6827 cancel: function () { return obj.canceled = true; }
6828 };
6829 if (update) { obj.update = function (from, to, text, origin) {
6830 if (from) { obj.from = clipPos(doc, from); }
6831 if (to) { obj.to = clipPos(doc, to); }
6832 if (text) { obj.text = text; }
6833 if (origin !== undefined) { obj.origin = origin; }
6834 }; }
6835 signal(doc, "beforeChange", doc, obj);
6836 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6837
6838 if (obj.canceled) {
6839 if (doc.cm) { doc.cm.curOp.updateInput = 2; }
6840 return null
6841 }
6842 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6843 }
6844
6845 // Apply a change to a document, and add it to the document's
6846 // history, and propagating it to all linked documents.
6847 function makeChange(doc, change, ignoreReadOnly) {
6848 if (doc.cm) {
6849 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6850 if (doc.cm.state.suppressEdits) { return }
6851 }
6852
6853 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6854 change = filterChange(doc, change, true);
6855 if (!change) { return }
6856 }
6857
6858 // Possibly split or suppress the update based on the presence
6859 // of read-only spans in its range.
6860 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6861 if (split) {
6862 for (var i = split.length - 1; i >= 0; --i)
6863 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6864 } else {
6865 makeChangeInner(doc, change);
6866 }
6867 }
6868
6869 function makeChangeInner(doc, change) {
6870 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6871 var selAfter = computeSelAfterChange(doc, change);
6872 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6873
6874 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6875 var rebased = [];
6876
6877 linkedDocs(doc, function (doc, sharedHist) {
6878 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6879 rebaseHist(doc.history, change);
6880 rebased.push(doc.history);
6881 }
6882 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6883 });
6884 }
6885
6886 // Revert a change stored in a document's history.
6887 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6888 var suppress = doc.cm && doc.cm.state.suppressEdits;
6889 if (suppress && !allowSelectionOnly) { return }
6890
6891 var hist = doc.history, event, selAfter = doc.sel;
6892 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6893
6894 // Verify that there is a useable event (so that ctrl-z won't
6895 // needlessly clear selection events)
6896 var i = 0;
6897 for (; i < source.length; i++) {
6898 event = source[i];
6899 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6900 { break }
6901 }
6902 if (i == source.length) { return }
6903 hist.lastOrigin = hist.lastSelOrigin = null;
6904
6905 for (;;) {
6906 event = source.pop();
6907 if (event.ranges) {
6908 pushSelectionToHistory(event, dest);
6909 if (allowSelectionOnly && !event.equals(doc.sel)) {
6910 setSelection(doc, event, {clearRedo: false});
6911 return
6912 }
6913 selAfter = event;
6914 } else if (suppress) {
6915 source.push(event);
6916 return
6917 } else { break }
6918 }
6919
6920 // Build up a reverse change object to add to the opposite history
6921 // stack (redo when undoing, and vice versa).
6922 var antiChanges = [];
6923 pushSelectionToHistory(selAfter, dest);
6924 dest.push({changes: antiChanges, generation: hist.generation});
6925 hist.generation = event.generation || ++hist.maxGeneration;
6926
6927 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6928
6929 var loop = function ( i ) {
6930 var change = event.changes[i];
6931 change.origin = type;
6932 if (filter && !filterChange(doc, change, false)) {
6933 source.length = 0;
6934 return {}
6935 }
6936
6937 antiChanges.push(historyChangeFromChange(doc, change));
6938
6939 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6940 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6941 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6942 var rebased = [];
6943
6944 // Propagate to the linked documents
6945 linkedDocs(doc, function (doc, sharedHist) {
6946 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6947 rebaseHist(doc.history, change);
6948 rebased.push(doc.history);
6949 }
6950 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6951 });
6952 };
6953
6954 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6955 var returned = loop( i$1 );
6956
6957 if ( returned ) return returned.v;
6958 }
6959 }
6960
6961 // Sub-views need their line numbers shifted when text is added
6962 // above or below them in the parent document.
6963 function shiftDoc(doc, distance) {
6964 if (distance == 0) { return }
6965 doc.first += distance;
6966 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6967 Pos(range.anchor.line + distance, range.anchor.ch),
6968 Pos(range.head.line + distance, range.head.ch)
6969 ); }), doc.sel.primIndex);
6970 if (doc.cm) {
6971 regChange(doc.cm, doc.first, doc.first - distance, distance);
6972 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6973 { regLineChange(doc.cm, l, "gutter"); }
6974 }
6975 }
6976
6977 // More lower-level change function, handling only a single document
6978 // (not linked ones).
6979 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6980 if (doc.cm && !doc.cm.curOp)
6981 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6982
6983 if (change.to.line < doc.first) {
6984 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6985 return
6986 }
6987 if (change.from.line > doc.lastLine()) { return }
6988
6989 // Clip the change to the size of this doc
6990 if (change.from.line < doc.first) {
6991 var shift = change.text.length - 1 - (doc.first - change.from.line);
6992 shiftDoc(doc, shift);
6993 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6994 text: [lst(change.text)], origin: change.origin};
6995 }
6996 var last = doc.lastLine();
6997 if (change.to.line > last) {
6998 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6999 text: [change.text[0]], origin: change.origin};
7000 }
7001
7002 change.removed = getBetween(doc, change.from, change.to);
7003
7004 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
7005 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
7006 else { updateDoc(doc, change, spans); }
7007 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
7008
7009 if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
7010 { doc.cantEdit = false; }
7011 }
7012
7013 // Handle the interaction of a change to a document with the editor
7014 // that this document is part of.
7015 function makeChangeSingleDocInEditor(cm, change, spans) {
7016 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
7017
7018 var recomputeMaxLength = false, checkWidthStart = from.line;
7019 if (!cm.options.lineWrapping) {
7020 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
7021 doc.iter(checkWidthStart, to.line + 1, function (line) {
7022 if (line == display.maxLine) {
7023 recomputeMaxLength = true;
7024 return true
7025 }
7026 });
7027 }
7028
7029 if (doc.sel.contains(change.from, change.to) > -1)
7030 { signalCursorActivity(cm); }
7031
7032 updateDoc(doc, change, spans, estimateHeight(cm));
7033
7034 if (!cm.options.lineWrapping) {
7035 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
7036 var len = lineLength(line);
7037 if (len > display.maxLineLength) {
7038 display.maxLine = line;
7039 display.maxLineLength = len;
7040 display.maxLineChanged = true;
7041 recomputeMaxLength = false;
7042 }
7043 });
7044 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
7045 }
7046
7047 retreatFrontier(doc, from.line);
7048 startWorker(cm, 400);
7049
7050 var lendiff = change.text.length - (to.line - from.line) - 1;
7051 // Remember that these lines changed, for updating the display
7052 if (change.full)
7053 { regChange(cm); }
7054 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
7055 { regLineChange(cm, from.line, "text"); }
7056 else
7057 { regChange(cm, from.line, to.line + 1, lendiff); }
7058
7059 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
7060 if (changeHandler || changesHandler) {
7061 var obj = {
7062 from: from, to: to,
7063 text: change.text,
7064 removed: change.removed,
7065 origin: change.origin
7066 };
7067 if (changeHandler) { signalLater(cm, "change", cm, obj); }
7068 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
7069 }
7070 cm.display.selForContextMenu = null;
7071 }
7072
7073 function replaceRange(doc, code, from, to, origin) {
7074 var assign;
7075
7076 if (!to) { to = from; }
7077 if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
7078 if (typeof code == "string") { code = doc.splitLines(code); }
7079 makeChange(doc, {from: from, to: to, text: code, origin: origin});
7080 }
7081
7082 // Rebasing/resetting history to deal with externally-sourced changes
7083
7084 function rebaseHistSelSingle(pos, from, to, diff) {
7085 if (to < pos.line) {
7086 pos.line += diff;
7087 } else if (from < pos.line) {
7088 pos.line = from;
7089 pos.ch = 0;
7090 }
7091 }
7092
7093 // Tries to rebase an array of history events given a change in the
7094 // document. If the change touches the same lines as the event, the
7095 // event, and everything 'behind' it, is discarded. If the change is
7096 // before the event, the event's positions are updated. Uses a
7097 // copy-on-write scheme for the positions, to avoid having to
7098 // reallocate them all on every rebase, but also avoid problems with
7099 // shared position objects being unsafely updated.
7100 function rebaseHistArray(array, from, to, diff) {
7101 for (var i = 0; i < array.length; ++i) {
7102 var sub = array[i], ok = true;
7103 if (sub.ranges) {
7104 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7105 for (var j = 0; j < sub.ranges.length; j++) {
7106 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7107 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7108 }
7109 continue
7110 }
7111 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
7112 var cur = sub.changes[j$1];
7113 if (to < cur.from.line) {
7114 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7115 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7116 } else if (from <= cur.to.line) {
7117 ok = false;
7118 break
7119 }
7120 }
7121 if (!ok) {
7122 array.splice(0, i + 1);
7123 i = 0;
7124 }
7125 }
7126 }
7127
7128 function rebaseHist(hist, change) {
7129 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7130 rebaseHistArray(hist.done, from, to, diff);
7131 rebaseHistArray(hist.undone, from, to, diff);
7132 }
7133
7134 // Utility for applying a change to a line by handle or number,
7135 // returning the number and optionally registering the line as
7136 // changed.
7137 function changeLine(doc, handle, changeType, op) {
7138 var no = handle, line = handle;
7139 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
7140 else { no = lineNo(handle); }
7141 if (no == null) { return null }
7142 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
7143 return line
7144 }
7145
7146 // The document is represented as a BTree consisting of leaves, with
7147 // chunk of lines in them, and branches, with up to ten leaves or
7148 // other branch nodes below them. The top node is always a branch
7149 // node, and is the document object itself (meaning it has
7150 // additional methods and properties).
7151 //
7152 // All nodes have parent links. The tree is used both to go from
7153 // line numbers to line objects, and to go from objects to numbers.
7154 // It also indexes by height, and is used to convert between height
7155 // and line object, and to find the total height of the document.
7156 //
7157 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7158
7159 function LeafChunk(lines) {
7160 this.lines = lines;
7161 this.parent = null;
7162 var height = 0;
7163 for (var i = 0; i < lines.length; ++i) {
7164 lines[i].parent = this;
7165 height += lines[i].height;
7166 }
7167 this.height = height;
7168 }
7169
7170 LeafChunk.prototype = {
7171 chunkSize: function() { return this.lines.length },
7172
7173 // Remove the n lines at offset 'at'.
7174 removeInner: function(at, n) {
7175 for (var i = at, e = at + n; i < e; ++i) {
7176 var line = this.lines[i];
7177 this.height -= line.height;
7178 cleanUpLine(line);
7179 signalLater(line, "delete");
7180 }
7181 this.lines.splice(at, n);
7182 },
7183
7184 // Helper used to collapse a small branch into a single leaf.
7185 collapse: function(lines) {
7186 lines.push.apply(lines, this.lines);
7187 },
7188
7189 // Insert the given array of lines at offset 'at', count them as
7190 // having the given height.
7191 insertInner: function(at, lines, height) {
7192 this.height += height;
7193 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7194 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }
7195 },
7196
7197 // Used to iterate over a part of the tree.
7198 iterN: function(at, n, op) {
7199 for (var e = at + n; at < e; ++at)
7200 { if (op(this.lines[at])) { return true } }
7201 }
7202 };
7203
7204 function BranchChunk(children) {
7205 this.children = children;
7206 var size = 0, height = 0;
7207 for (var i = 0; i < children.length; ++i) {
7208 var ch = children[i];
7209 size += ch.chunkSize(); height += ch.height;
7210 ch.parent = this;
7211 }
7212 this.size = size;
7213 this.height = height;
7214 this.parent = null;
7215 }
7216
7217 BranchChunk.prototype = {
7218 chunkSize: function() { return this.size },
7219
7220 removeInner: function(at, n) {
7221 this.size -= n;
7222 for (var i = 0; i < this.children.length; ++i) {
7223 var child = this.children[i], sz = child.chunkSize();
7224 if (at < sz) {
7225 var rm = Math.min(n, sz - at), oldHeight = child.height;
7226 child.removeInner(at, rm);
7227 this.height -= oldHeight - child.height;
7228 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7229 if ((n -= rm) == 0) { break }
7230 at = 0;
7231 } else { at -= sz; }
7232 }
7233 // If the result is smaller than 25 lines, ensure that it is a
7234 // single leaf node.
7235 if (this.size - n < 25 &&
7236 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7237 var lines = [];
7238 this.collapse(lines);
7239 this.children = [new LeafChunk(lines)];
7240 this.children[0].parent = this;
7241 }
7242 },
7243
7244 collapse: function(lines) {
7245 for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }
7246 },
7247
7248 insertInner: function(at, lines, height) {
7249 this.size += lines.length;
7250 this.height += height;
7251 for (var i = 0; i < this.children.length; ++i) {
7252 var child = this.children[i], sz = child.chunkSize();
7253 if (at <= sz) {
7254 child.insertInner(at, lines, height);
7255 if (child.lines && child.lines.length > 50) {
7256 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7257 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7258 var remaining = child.lines.length % 25 + 25;
7259 for (var pos = remaining; pos < child.lines.length;) {
7260 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7261 child.height -= leaf.height;
7262 this.children.splice(++i, 0, leaf);
7263 leaf.parent = this;
7264 }
7265 child.lines = child.lines.slice(0, remaining);
7266 this.maybeSpill();
7267 }
7268 break
7269 }
7270 at -= sz;
7271 }
7272 },
7273
7274 // When a node has grown, check whether it should be split.
7275 maybeSpill: function() {
7276 if (this.children.length <= 10) { return }
7277 var me = this;
7278 do {
7279 var spilled = me.children.splice(me.children.length - 5, 5);
7280 var sibling = new BranchChunk(spilled);
7281 if (!me.parent) { // Become the parent node
7282 var copy = new BranchChunk(me.children);
7283 copy.parent = me;
7284 me.children = [copy, sibling];
7285 me = copy;
7286 } else {
7287 me.size -= sibling.size;
7288 me.height -= sibling.height;
7289 var myIndex = indexOf(me.parent.children, me);
7290 me.parent.children.splice(myIndex + 1, 0, sibling);
7291 }
7292 sibling.parent = me.parent;
7293 } while (me.children.length > 10)
7294 me.parent.maybeSpill();
7295 },
7296
7297 iterN: function(at, n, op) {
7298 for (var i = 0; i < this.children.length; ++i) {
7299 var child = this.children[i], sz = child.chunkSize();
7300 if (at < sz) {
7301 var used = Math.min(n, sz - at);
7302 if (child.iterN(at, used, op)) { return true }
7303 if ((n -= used) == 0) { break }
7304 at = 0;
7305 } else { at -= sz; }
7306 }
7307 }
7308 };
7309
7310 // Line widgets are block elements displayed above or below a line.
7311
7312 var LineWidget = function(doc, node, options) {
7313 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7314 { this[opt] = options[opt]; } } }
7315 this.doc = doc;
7316 this.node = node;
7317 };
7318
7319 LineWidget.prototype.clear = function () {
7320 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7321 if (no == null || !ws) { return }
7322 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }
7323 if (!ws.length) { line.widgets = null; }
7324 var height = widgetHeight(this);
7325 updateLineHeight(line, Math.max(0, line.height - height));
7326 if (cm) {
7327 runInOp(cm, function () {
7328 adjustScrollWhenAboveVisible(cm, line, -height);
7329 regLineChange(cm, no, "widget");
7330 });
7331 signalLater(cm, "lineWidgetCleared", cm, this, no);
7332 }
7333 };
7334
7335 LineWidget.prototype.changed = function () {
7336 var this$1 = this;
7337
7338 var oldH = this.height, cm = this.doc.cm, line = this.line;
7339 this.height = null;
7340 var diff = widgetHeight(this) - oldH;
7341 if (!diff) { return }
7342 if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
7343 if (cm) {
7344 runInOp(cm, function () {
7345 cm.curOp.forceUpdate = true;
7346 adjustScrollWhenAboveVisible(cm, line, diff);
7347 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7348 });
7349 }
7350 };
7351 eventMixin(LineWidget);
7352
7353 function adjustScrollWhenAboveVisible(cm, line, diff) {
7354 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7355 { addToScrollTop(cm, diff); }
7356 }
7357
7358 function addLineWidget(doc, handle, node, options) {
7359 var widget = new LineWidget(doc, node, options);
7360 var cm = doc.cm;
7361 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7362 changeLine(doc, handle, "widget", function (line) {
7363 var widgets = line.widgets || (line.widgets = []);
7364 if (widget.insertAt == null) { widgets.push(widget); }
7365 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7366 widget.line = line;
7367 if (cm && !lineIsHidden(doc, line)) {
7368 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7369 updateLineHeight(line, line.height + widgetHeight(widget));
7370 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7371 cm.curOp.forceUpdate = true;
7372 }
7373 return true
7374 });
7375 if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
7376 return widget
7377 }
7378
7379 // TEXTMARKERS
7380
7381 // Created with markText and setBookmark methods. A TextMarker is a
7382 // handle that can be used to clear or find a marked position in the
7383 // document. Line objects hold arrays (markedSpans) containing
7384 // {from, to, marker} object pointing to such marker objects, and
7385 // indicating that such a marker is present on that line. Multiple
7386 // lines may point to the same marker when it spans across lines.
7387 // The spans will have null for their from/to properties when the
7388 // marker continues beyond the start/end of the line. Markers have
7389 // links back to the lines they currently touch.
7390
7391 // Collapsed markers have unique ids, in order to be able to order
7392 // them, which is needed for uniquely determining an outer marker
7393 // when they overlap (they may nest, but not partially overlap).
7394 var nextMarkerId = 0;
7395
7396 var TextMarker = function(doc, type) {
7397 this.lines = [];
7398 this.type = type;
7399 this.doc = doc;
7400 this.id = ++nextMarkerId;
7401 };
7402
7403 // Clear the marker.
7404 TextMarker.prototype.clear = function () {
7405 if (this.explicitlyCleared) { return }
7406 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7407 if (withOp) { startOperation(cm); }
7408 if (hasHandler(this, "clear")) {
7409 var found = this.find();
7410 if (found) { signalLater(this, "clear", found.from, found.to); }
7411 }
7412 var min = null, max = null;
7413 for (var i = 0; i < this.lines.length; ++i) {
7414 var line = this.lines[i];
7415 var span = getMarkedSpanFor(line.markedSpans, this);
7416 if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7417 else if (cm) {
7418 if (span.to != null) { max = lineNo(line); }
7419 if (span.from != null) { min = lineNo(line); }
7420 }
7421 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7422 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
7423 { updateLineHeight(line, textHeight(cm.display)); }
7424 }
7425 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7426 var visual = visualLine(this.lines[i$1]), len = lineLength(visual);
7427 if (len > cm.display.maxLineLength) {
7428 cm.display.maxLine = visual;
7429 cm.display.maxLineLength = len;
7430 cm.display.maxLineChanged = true;
7431 }
7432 } }
7433
7434 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7435 this.lines.length = 0;
7436 this.explicitlyCleared = true;
7437 if (this.atomic && this.doc.cantEdit) {
7438 this.doc.cantEdit = false;
7439 if (cm) { reCheckSelection(cm.doc); }
7440 }
7441 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7442 if (withOp) { endOperation(cm); }
7443 if (this.parent) { this.parent.clear(); }
7444 };
7445
7446 // Find the position of the marker in the document. Returns a {from,
7447 // to} object by default. Side can be passed to get a specific side
7448 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7449 // Pos objects returned contain a line object, rather than a line
7450 // number (used to prevent looking up the same line twice).
7451 TextMarker.prototype.find = function (side, lineObj) {
7452 if (side == null && this.type == "bookmark") { side = 1; }
7453 var from, to;
7454 for (var i = 0; i < this.lines.length; ++i) {
7455 var line = this.lines[i];
7456 var span = getMarkedSpanFor(line.markedSpans, this);
7457 if (span.from != null) {
7458 from = Pos(lineObj ? line : lineNo(line), span.from);
7459 if (side == -1) { return from }
7460 }
7461 if (span.to != null) {
7462 to = Pos(lineObj ? line : lineNo(line), span.to);
7463 if (side == 1) { return to }
7464 }
7465 }
7466 return from && {from: from, to: to}
7467 };
7468
7469 // Signals that the marker's widget changed, and surrounding layout
7470 // should be recomputed.
7471 TextMarker.prototype.changed = function () {
7472 var this$1 = this;
7473
7474 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7475 if (!pos || !cm) { return }
7476 runInOp(cm, function () {
7477 var line = pos.line, lineN = lineNo(pos.line);
7478 var view = findViewForLine(cm, lineN);
7479 if (view) {
7480 clearLineMeasurementCacheFor(view);
7481 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7482 }
7483 cm.curOp.updateMaxLine = true;
7484 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7485 var oldHeight = widget.height;
7486 widget.height = null;
7487 var dHeight = widgetHeight(widget) - oldHeight;
7488 if (dHeight)
7489 { updateLineHeight(line, line.height + dHeight); }
7490 }
7491 signalLater(cm, "markerChanged", cm, this$1);
7492 });
7493 };
7494
7495 TextMarker.prototype.attachLine = function (line) {
7496 if (!this.lines.length && this.doc.cm) {
7497 var op = this.doc.cm.curOp;
7498 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7499 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7500 }
7501 this.lines.push(line);
7502 };
7503
7504 TextMarker.prototype.detachLine = function (line) {
7505 this.lines.splice(indexOf(this.lines, line), 1);
7506 if (!this.lines.length && this.doc.cm) {
7507 var op = this.doc.cm.curOp
7508 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7509 }
7510 };
7511 eventMixin(TextMarker);
7512
7513 // Create a marker, wire it up to the right lines, and
7514 function markText(doc, from, to, options, type) {
7515 // Shared markers (across linked documents) are handled separately
7516 // (markTextShared will call out to this again, once per
7517 // document).
7518 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7519 // Ensure we are in an operation.
7520 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7521
7522 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7523 if (options) { copyObj(options, marker, false); }
7524 // Don't connect empty markers unless clearWhenEmpty is false
7525 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7526 { return marker }
7527 if (marker.replacedWith) {
7528 // Showing up as a widget implies collapsed (widget replaces text)
7529 marker.collapsed = true;
7530 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7531 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7532 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7533 }
7534 if (marker.collapsed) {
7535 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7536 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7537 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7538 seeCollapsedSpans();
7539 }
7540
7541 if (marker.addToHistory)
7542 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7543
7544 var curLine = from.line, cm = doc.cm, updateMaxLine;
7545 doc.iter(curLine, to.line + 1, function (line) {
7546 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7547 { updateMaxLine = true; }
7548 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7549 addMarkedSpan(line, new MarkedSpan(marker,
7550 curLine == from.line ? from.ch : null,
7551 curLine == to.line ? to.ch : null));
7552 ++curLine;
7553 });
7554 // lineIsHidden depends on the presence of the spans, so needs a second pass
7555 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7556 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7557 }); }
7558
7559 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7560
7561 if (marker.readOnly) {
7562 seeReadOnlySpans();
7563 if (doc.history.done.length || doc.history.undone.length)
7564 { doc.clearHistory(); }
7565 }
7566 if (marker.collapsed) {
7567 marker.id = ++nextMarkerId;
7568 marker.atomic = true;
7569 }
7570 if (cm) {
7571 // Sync editor state
7572 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7573 if (marker.collapsed)
7574 { regChange(cm, from.line, to.line + 1); }
7575 else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
7576 marker.attributes || marker.title)
7577 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7578 if (marker.atomic) { reCheckSelection(cm.doc); }
7579 signalLater(cm, "markerAdded", cm, marker);
7580 }
7581 return marker
7582 }
7583
7584 // SHARED TEXTMARKERS
7585
7586 // A shared marker spans multiple linked documents. It is
7587 // implemented as a meta-marker-object controlling multiple normal
7588 // markers.
7589 var SharedTextMarker = function(markers, primary) {
7590 this.markers = markers;
7591 this.primary = primary;
7592 for (var i = 0; i < markers.length; ++i)
7593 { markers[i].parent = this; }
7594 };
7595
7596 SharedTextMarker.prototype.clear = function () {
7597 if (this.explicitlyCleared) { return }
7598 this.explicitlyCleared = true;
7599 for (var i = 0; i < this.markers.length; ++i)
7600 { this.markers[i].clear(); }
7601 signalLater(this, "clear");
7602 };
7603
7604 SharedTextMarker.prototype.find = function (side, lineObj) {
7605 return this.primary.find(side, lineObj)
7606 };
7607 eventMixin(SharedTextMarker);
7608
7609 function markTextShared(doc, from, to, options, type) {
7610 options = copyObj(options);
7611 options.shared = false;
7612 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7613 var widget = options.widgetNode;
7614 linkedDocs(doc, function (doc) {
7615 if (widget) { options.widgetNode = widget.cloneNode(true); }
7616 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7617 for (var i = 0; i < doc.linked.length; ++i)
7618 { if (doc.linked[i].isParent) { return } }
7619 primary = lst(markers);
7620 });
7621 return new SharedTextMarker(markers, primary)
7622 }
7623
7624 function findSharedMarkers(doc) {
7625 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7626 }
7627
7628 function copySharedMarkers(doc, markers) {
7629 for (var i = 0; i < markers.length; i++) {
7630 var marker = markers[i], pos = marker.find();
7631 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7632 if (cmp(mFrom, mTo)) {
7633 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7634 marker.markers.push(subMark);
7635 subMark.parent = marker;
7636 }
7637 }
7638 }
7639
7640 function detachSharedMarkers(markers) {
7641 var loop = function ( i ) {
7642 var marker = markers[i], linked = [marker.primary.doc];
7643 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7644 for (var j = 0; j < marker.markers.length; j++) {
7645 var subMarker = marker.markers[j];
7646 if (indexOf(linked, subMarker.doc) == -1) {
7647 subMarker.parent = null;
7648 marker.markers.splice(j--, 1);
7649 }
7650 }
7651 };
7652
7653 for (var i = 0; i < markers.length; i++) loop( i );
7654 }
7655
7656 var nextDocId = 0;
7657 var Doc = function(text, mode, firstLine, lineSep, direction) {
7658 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7659 if (firstLine == null) { firstLine = 0; }
7660
7661 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7662 this.first = firstLine;
7663 this.scrollTop = this.scrollLeft = 0;
7664 this.cantEdit = false;
7665 this.cleanGeneration = 1;
7666 this.modeFrontier = this.highlightFrontier = firstLine;
7667 var start = Pos(firstLine, 0);
7668 this.sel = simpleSelection(start);
7669 this.history = new History(null);
7670 this.id = ++nextDocId;
7671 this.modeOption = mode;
7672 this.lineSep = lineSep;
7673 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7674 this.extend = false;
7675
7676 if (typeof text == "string") { text = this.splitLines(text); }
7677 updateDoc(this, {from: start, to: start, text: text});
7678 setSelection(this, simpleSelection(start), sel_dontScroll);
7679 };
7680
7681 Doc.prototype = createObj(BranchChunk.prototype, {
7682 constructor: Doc,
7683 // Iterate over the document. Supports two forms -- with only one
7684 // argument, it calls that for each line in the document. With
7685 // three, it iterates over the range given by the first two (with
7686 // the second being non-inclusive).
7687 iter: function(from, to, op) {
7688 if (op) { this.iterN(from - this.first, to - from, op); }
7689 else { this.iterN(this.first, this.first + this.size, from); }
7690 },
7691
7692 // Non-public interface for adding and removing lines.
7693 insert: function(at, lines) {
7694 var height = 0;
7695 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7696 this.insertInner(at - this.first, lines, height);
7697 },
7698 remove: function(at, n) { this.removeInner(at - this.first, n); },
7699
7700 // From here, the methods are part of the public interface. Most
7701 // are also available from CodeMirror (editor) instances.
7702
7703 getValue: function(lineSep) {
7704 var lines = getLines(this, this.first, this.first + this.size);
7705 if (lineSep === false) { return lines }
7706 return lines.join(lineSep || this.lineSeparator())
7707 },
7708 setValue: docMethodOp(function(code) {
7709 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7710 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7711 text: this.splitLines(code), origin: "setValue", full: true}, true);
7712 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7713 setSelection(this, simpleSelection(top), sel_dontScroll);
7714 }),
7715 replaceRange: function(code, from, to, origin) {
7716 from = clipPos(this, from);
7717 to = to ? clipPos(this, to) : from;
7718 replaceRange(this, code, from, to, origin);
7719 },
7720 getRange: function(from, to, lineSep) {
7721 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7722 if (lineSep === false) { return lines }
7723 return lines.join(lineSep || this.lineSeparator())
7724 },
7725
7726 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7727
7728 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7729 getLineNumber: function(line) {return lineNo(line)},
7730
7731 getLineHandleVisualStart: function(line) {
7732 if (typeof line == "number") { line = getLine(this, line); }
7733 return visualLine(line)
7734 },
7735
7736 lineCount: function() {return this.size},
7737 firstLine: function() {return this.first},
7738 lastLine: function() {return this.first + this.size - 1},
7739
7740 clipPos: function(pos) {return clipPos(this, pos)},
7741
7742 getCursor: function(start) {
7743 var range = this.sel.primary(), pos;
7744 if (start == null || start == "head") { pos = range.head; }
7745 else if (start == "anchor") { pos = range.anchor; }
7746 else if (start == "end" || start == "to" || start === false) { pos = range.to(); }
7747 else { pos = range.from(); }
7748 return pos
7749 },
7750 listSelections: function() { return this.sel.ranges },
7751 somethingSelected: function() {return this.sel.somethingSelected()},
7752
7753 setCursor: docMethodOp(function(line, ch, options) {
7754 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7755 }),
7756 setSelection: docMethodOp(function(anchor, head, options) {
7757 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7758 }),
7759 extendSelection: docMethodOp(function(head, other, options) {
7760 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7761 }),
7762 extendSelections: docMethodOp(function(heads, options) {
7763 extendSelections(this, clipPosArray(this, heads), options);
7764 }),
7765 extendSelectionsBy: docMethodOp(function(f, options) {
7766 var heads = map(this.sel.ranges, f);
7767 extendSelections(this, clipPosArray(this, heads), options);
7768 }),
7769 setSelections: docMethodOp(function(ranges, primary, options) {
7770 if (!ranges.length) { return }
7771 var out = [];
7772 for (var i = 0; i < ranges.length; i++)
7773 { out[i] = new Range(clipPos(this, ranges[i].anchor),
7774 clipPos(this, ranges[i].head)); }
7775 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7776 setSelection(this, normalizeSelection(this.cm, out, primary), options);
7777 }),
7778 addSelection: docMethodOp(function(anchor, head, options) {
7779 var ranges = this.sel.ranges.slice(0);
7780 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7781 setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
7782 }),
7783
7784 getSelection: function(lineSep) {
7785 var ranges = this.sel.ranges, lines;
7786 for (var i = 0; i < ranges.length; i++) {
7787 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7788 lines = lines ? lines.concat(sel) : sel;
7789 }
7790 if (lineSep === false) { return lines }
7791 else { return lines.join(lineSep || this.lineSeparator()) }
7792 },
7793 getSelections: function(lineSep) {
7794 var parts = [], ranges = this.sel.ranges;
7795 for (var i = 0; i < ranges.length; i++) {
7796 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7797 if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }
7798 parts[i] = sel;
7799 }
7800 return parts
7801 },
7802 replaceSelection: function(code, collapse, origin) {
7803 var dup = [];
7804 for (var i = 0; i < this.sel.ranges.length; i++)
7805 { dup[i] = code; }
7806 this.replaceSelections(dup, collapse, origin || "+input");
7807 },
7808 replaceSelections: docMethodOp(function(code, collapse, origin) {
7809 var changes = [], sel = this.sel;
7810 for (var i = 0; i < sel.ranges.length; i++) {
7811 var range = sel.ranges[i];
7812 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
7813 }
7814 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7815 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7816 { makeChange(this, changes[i$1]); }
7817 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7818 else if (this.cm) { ensureCursorVisible(this.cm); }
7819 }),
7820 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7821 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7822 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7823 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7824
7825 setExtending: function(val) {this.extend = val;},
7826 getExtending: function() {return this.extend},
7827
7828 historySize: function() {
7829 var hist = this.history, done = 0, undone = 0;
7830 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7831 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7832 return {undo: done, redo: undone}
7833 },
7834 clearHistory: function() {
7835 var this$1 = this;
7836
7837 this.history = new History(this.history.maxGeneration);
7838 linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
7839 },
7840
7841 markClean: function() {
7842 this.cleanGeneration = this.changeGeneration(true);
7843 },
7844 changeGeneration: function(forceSplit) {
7845 if (forceSplit)
7846 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7847 return this.history.generation
7848 },
7849 isClean: function (gen) {
7850 return this.history.generation == (gen || this.cleanGeneration)
7851 },
7852
7853 getHistory: function() {
7854 return {done: copyHistoryArray(this.history.done),
7855 undone: copyHistoryArray(this.history.undone)}
7856 },
7857 setHistory: function(histData) {
7858 var hist = this.history = new History(this.history.maxGeneration);
7859 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7860 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7861 },
7862
7863 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7864 return changeLine(this, line, "gutter", function (line) {
7865 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7866 markers[gutterID] = value;
7867 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7868 return true
7869 })
7870 }),
7871
7872 clearGutter: docMethodOp(function(gutterID) {
7873 var this$1 = this;
7874
7875 this.iter(function (line) {
7876 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7877 changeLine(this$1, line, "gutter", function () {
7878 line.gutterMarkers[gutterID] = null;
7879 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7880 return true
7881 });
7882 }
7883 });
7884 }),
7885
7886 lineInfo: function(line) {
7887 var n;
7888 if (typeof line == "number") {
7889 if (!isLine(this, line)) { return null }
7890 n = line;
7891 line = getLine(this, line);
7892 if (!line) { return null }
7893 } else {
7894 n = lineNo(line);
7895 if (n == null) { return null }
7896 }
7897 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7898 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7899 widgets: line.widgets}
7900 },
7901
7902 addLineClass: docMethodOp(function(handle, where, cls) {
7903 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7904 var prop = where == "text" ? "textClass"
7905 : where == "background" ? "bgClass"
7906 : where == "gutter" ? "gutterClass" : "wrapClass";
7907 if (!line[prop]) { line[prop] = cls; }
7908 else if (classTest(cls).test(line[prop])) { return false }
7909 else { line[prop] += " " + cls; }
7910 return true
7911 })
7912 }),
7913 removeLineClass: docMethodOp(function(handle, where, cls) {
7914 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7915 var prop = where == "text" ? "textClass"
7916 : where == "background" ? "bgClass"
7917 : where == "gutter" ? "gutterClass" : "wrapClass";
7918 var cur = line[prop];
7919 if (!cur) { return false }
7920 else if (cls == null) { line[prop] = null; }
7921 else {
7922 var found = cur.match(classTest(cls));
7923 if (!found) { return false }
7924 var end = found.index + found[0].length;
7925 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7926 }
7927 return true
7928 })
7929 }),
7930
7931 addLineWidget: docMethodOp(function(handle, node, options) {
7932 return addLineWidget(this, handle, node, options)
7933 }),
7934 removeLineWidget: function(widget) { widget.clear(); },
7935
7936 markText: function(from, to, options) {
7937 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7938 },
7939 setBookmark: function(pos, options) {
7940 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7941 insertLeft: options && options.insertLeft,
7942 clearWhenEmpty: false, shared: options && options.shared,
7943 handleMouseEvents: options && options.handleMouseEvents};
7944 pos = clipPos(this, pos);
7945 return markText(this, pos, pos, realOpts, "bookmark")
7946 },
7947 findMarksAt: function(pos) {
7948 pos = clipPos(this, pos);
7949 var markers = [], spans = getLine(this, pos.line).markedSpans;
7950 if (spans) { for (var i = 0; i < spans.length; ++i) {
7951 var span = spans[i];
7952 if ((span.from == null || span.from <= pos.ch) &&
7953 (span.to == null || span.to >= pos.ch))
7954 { markers.push(span.marker.parent || span.marker); }
7955 } }
7956 return markers
7957 },
7958 findMarks: function(from, to, filter) {
7959 from = clipPos(this, from); to = clipPos(this, to);
7960 var found = [], lineNo = from.line;
7961 this.iter(from.line, to.line + 1, function (line) {
7962 var spans = line.markedSpans;
7963 if (spans) { for (var i = 0; i < spans.length; i++) {
7964 var span = spans[i];
7965 if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
7966 span.from == null && lineNo != from.line ||
7967 span.from != null && lineNo == to.line && span.from >= to.ch) &&
7968 (!filter || filter(span.marker)))
7969 { found.push(span.marker.parent || span.marker); }
7970 } }
7971 ++lineNo;
7972 });
7973 return found
7974 },
7975 getAllMarks: function() {
7976 var markers = [];
7977 this.iter(function (line) {
7978 var sps = line.markedSpans;
7979 if (sps) { for (var i = 0; i < sps.length; ++i)
7980 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7981 });
7982 return markers
7983 },
7984
7985 posFromIndex: function(off) {
7986 var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
7987 this.iter(function (line) {
7988 var sz = line.text.length + sepSize;
7989 if (sz > off) { ch = off; return true }
7990 off -= sz;
7991 ++lineNo;
7992 });
7993 return clipPos(this, Pos(lineNo, ch))
7994 },
7995 indexFromPos: function (coords) {
7996 coords = clipPos(this, coords);
7997 var index = coords.ch;
7998 if (coords.line < this.first || coords.ch < 0) { return 0 }
7999 var sepSize = this.lineSeparator().length;
8000 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
8001 index += line.text.length + sepSize;
8002 });
8003 return index
8004 },
8005
8006 copy: function(copyHistory) {
8007 var doc = new Doc(getLines(this, this.first, this.first + this.size),
8008 this.modeOption, this.first, this.lineSep, this.direction);
8009 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
8010 doc.sel = this.sel;
8011 doc.extend = false;
8012 if (copyHistory) {
8013 doc.history.undoDepth = this.history.undoDepth;
8014 doc.setHistory(this.getHistory());
8015 }
8016 return doc
8017 },
8018
8019 linkedDoc: function(options) {
8020 if (!options) { options = {}; }
8021 var from = this.first, to = this.first + this.size;
8022 if (options.from != null && options.from > from) { from = options.from; }
8023 if (options.to != null && options.to < to) { to = options.to; }
8024 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
8025 if (options.sharedHist) { copy.history = this.history
8026 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
8027 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
8028 copySharedMarkers(copy, findSharedMarkers(this));
8029 return copy
8030 },
8031 unlinkDoc: function(other) {
8032 if (other instanceof CodeMirror) { other = other.doc; }
8033 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
8034 var link = this.linked[i];
8035 if (link.doc != other) { continue }
8036 this.linked.splice(i, 1);
8037 other.unlinkDoc(this);
8038 detachSharedMarkers(findSharedMarkers(this));
8039 break
8040 } }
8041 // If the histories were shared, split them again
8042 if (other.history == this.history) {
8043 var splitIds = [other.id];
8044 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
8045 other.history = new History(null);
8046 other.history.done = copyHistoryArray(this.history.done, splitIds);
8047 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
8048 }
8049 },
8050 iterLinkedDocs: function(f) {linkedDocs(this, f);},
8051
8052 getMode: function() {return this.mode},
8053 getEditor: function() {return this.cm},
8054
8055 splitLines: function(str) {
8056 if (this.lineSep) { return str.split(this.lineSep) }
8057 return splitLinesAuto(str)
8058 },
8059 lineSeparator: function() { return this.lineSep || "\n" },
8060
8061 setDirection: docMethodOp(function (dir) {
8062 if (dir != "rtl") { dir = "ltr"; }
8063 if (dir == this.direction) { return }
8064 this.direction = dir;
8065 this.iter(function (line) { return line.order = null; });
8066 if (this.cm) { directionChanged(this.cm); }
8067 })
8068 });
8069
8070 // Public alias.
8071 Doc.prototype.eachLine = Doc.prototype.iter;
8072
8073 // Kludge to work around strange IE behavior where it'll sometimes
8074 // re-fire a series of drag-related events right after the drop (#1551)
8075 var lastDrop = 0;
8076
8077 function onDrop(e) {
8078 var cm = this;
8079 clearDragCursor(cm);
8080 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
8081 { return }
8082 e_preventDefault(e);
8083 if (ie) { lastDrop = +new Date; }
8084 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
8085 if (!pos || cm.isReadOnly()) { return }
8086 // Might be a file drop, in which case we simply extract the text
8087 // and insert it.
8088 if (files && files.length && window.FileReader && window.File) {
8089 var n = files.length, text = Array(n), read = 0;
8090 var markAsReadAndPasteIfAllFilesAreRead = function () {
8091 if (++read == n) {
8092 operation(cm, function () {
8093 pos = clipPos(cm.doc, pos);
8094 var change = {from: pos, to: pos,
8095 text: cm.doc.splitLines(
8096 text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
8097 origin: "paste"};
8098 makeChange(cm.doc, change);
8099 setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
8100 })();
8101 }
8102 };
8103 var readTextFromFile = function (file, i) {
8104 if (cm.options.allowDropFileTypes &&
8105 indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
8106 markAsReadAndPasteIfAllFilesAreRead();
8107 return
8108 }
8109 var reader = new FileReader;
8110 reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
8111 reader.onload = function () {
8112 var content = reader.result;
8113 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
8114 markAsReadAndPasteIfAllFilesAreRead();
8115 return
8116 }
8117 text[i] = content;
8118 markAsReadAndPasteIfAllFilesAreRead();
8119 };
8120 reader.readAsText(file);
8121 };
8122 for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
8123 } else { // Normal drop
8124 // Don't do a replace if the drop happened inside of the selected text.
8125 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
8126 cm.state.draggingText(e);
8127 // Ensure the editor is re-focused
8128 setTimeout(function () { return cm.display.input.focus(); }, 20);
8129 return
8130 }
8131 try {
8132 var text$1 = e.dataTransfer.getData("Text");
8133 if (text$1) {
8134 var selected;
8135 if (cm.state.draggingText && !cm.state.draggingText.copy)
8136 { selected = cm.listSelections(); }
8137 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
8138 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
8139 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
8140 cm.replaceSelection(text$1, "around", "paste");
8141 cm.display.input.focus();
8142 }
8143 }
8144 catch(e){}
8145 }
8146 }
8147
8148 function onDragStart(cm, e) {
8149 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
8150 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
8151
8152 e.dataTransfer.setData("Text", cm.getSelection());
8153 e.dataTransfer.effectAllowed = "copyMove";
8154
8155 // Use dummy image instead of default browsers image.
8156 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
8157 if (e.dataTransfer.setDragImage && !safari) {
8158 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
8159 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
8160 if (presto) {
8161 img.width = img.height = 1;
8162 cm.display.wrapper.appendChild(img);
8163 // Force a relayout, or Opera won't use our image for some obscure reason
8164 img._top = img.offsetTop;
8165 }
8166 e.dataTransfer.setDragImage(img, 0, 0);
8167 if (presto) { img.parentNode.removeChild(img); }
8168 }
8169 }
8170
8171 function onDragOver(cm, e) {
8172 var pos = posFromMouse(cm, e);
8173 if (!pos) { return }
8174 var frag = document.createDocumentFragment();
8175 drawSelectionCursor(cm, pos, frag);
8176 if (!cm.display.dragCursor) {
8177 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8178 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8179 }
8180 removeChildrenAndAdd(cm.display.dragCursor, frag);
8181 }
8182
8183 function clearDragCursor(cm) {
8184 if (cm.display.dragCursor) {
8185 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8186 cm.display.dragCursor = null;
8187 }
8188 }
8189
8190 // These must be handled carefully, because naively registering a
8191 // handler for each editor will cause the editors to never be
8192 // garbage collected.
8193
8194 function forEachCodeMirror(f) {
8195 if (!document.getElementsByClassName) { return }
8196 var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
8197 for (var i = 0; i < byClass.length; i++) {
8198 var cm = byClass[i].CodeMirror;
8199 if (cm) { editors.push(cm); }
8200 }
8201 if (editors.length) { editors[0].operation(function () {
8202 for (var i = 0; i < editors.length; i++) { f(editors[i]); }
8203 }); }
8204 }
8205
8206 var globalsRegistered = false;
8207 function ensureGlobalHandlers() {
8208 if (globalsRegistered) { return }
8209 registerGlobalHandlers();
8210 globalsRegistered = true;
8211 }
8212 function registerGlobalHandlers() {
8213 // When the window resizes, we need to refresh active editors.
8214 var resizeTimer;
8215 on(window, "resize", function () {
8216 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8217 resizeTimer = null;
8218 forEachCodeMirror(onResize);
8219 }, 100); }
8220 });
8221 // When the window loses focus, we want to show the editor as blurred
8222 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8223 }
8224 // Called when the window resizes
8225 function onResize(cm) {
8226 var d = cm.display;
8227 // Might be a text scaling operation, clear size caches.
8228 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8229 d.scrollbarsClipped = false;
8230 cm.setSize();
8231 }
8232
8233 var keyNames = {
8234 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8235 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8236 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8237 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8238 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
8239 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8240 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8241 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8242 };
8243
8244 // Number keys
8245 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8246 // Alphabetic keys
8247 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8248 // Function keys
8249 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8250
8251 var keyMap = {};
8252
8253 keyMap.basic = {
8254 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8255 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8256 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8257 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8258 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8259 "Esc": "singleSelection"
8260 };
8261 // Note that the save and find-related commands aren't defined by
8262 // default. User code or addons can define them. Unknown commands
8263 // are simply ignored.
8264 keyMap.pcDefault = {
8265 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8266 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8267 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8268 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8269 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8270 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8271 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8272 "fallthrough": "basic"
8273 };
8274 // Very basic readline/emacs-style bindings, which are standard on Mac.
8275 keyMap.emacsy = {
8276 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8277 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8278 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8279 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8280 "Ctrl-O": "openLine"
8281 };
8282 keyMap.macDefault = {
8283 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8284 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8285 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8286 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8287 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8288 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8289 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8290 "fallthrough": ["basic", "emacsy"]
8291 };
8292 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8293
8294 // KEYMAP DISPATCH
8295
8296 function normalizeKeyName(name) {
8297 var parts = name.split(/-(?!$)/);
8298 name = parts[parts.length - 1];
8299 var alt, ctrl, shift, cmd;
8300 for (var i = 0; i < parts.length - 1; i++) {
8301 var mod = parts[i];
8302 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8303 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8304 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8305 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8306 else { throw new Error("Unrecognized modifier name: " + mod) }
8307 }
8308 if (alt) { name = "Alt-" + name; }
8309 if (ctrl) { name = "Ctrl-" + name; }
8310 if (cmd) { name = "Cmd-" + name; }
8311 if (shift) { name = "Shift-" + name; }
8312 return name
8313 }
8314
8315 // This is a kludge to keep keymaps mostly working as raw objects
8316 // (backwards compatibility) while at the same time support features
8317 // like normalization and multi-stroke key bindings. It compiles a
8318 // new normalized keymap, and then updates the old object to reflect
8319 // this.
8320 function normalizeKeyMap(keymap) {
8321 var copy = {};
8322 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8323 var value = keymap[keyname];
8324 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8325 if (value == "...") { delete keymap[keyname]; continue }
8326
8327 var keys = map(keyname.split(" "), normalizeKeyName);
8328 for (var i = 0; i < keys.length; i++) {
8329 var val = (void 0), name = (void 0);
8330 if (i == keys.length - 1) {
8331 name = keys.join(" ");
8332 val = value;
8333 } else {
8334 name = keys.slice(0, i + 1).join(" ");
8335 val = "...";
8336 }
8337 var prev = copy[name];
8338 if (!prev) { copy[name] = val; }
8339 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8340 }
8341 delete keymap[keyname];
8342 } }
8343 for (var prop in copy) { keymap[prop] = copy[prop]; }
8344 return keymap
8345 }
8346
8347 function lookupKey(key, map, handle, context) {
8348 map = getKeyMap(map);
8349 var found = map.call ? map.call(key, context) : map[key];
8350 if (found === false) { return "nothing" }
8351 if (found === "...") { return "multi" }
8352 if (found != null && handle(found)) { return "handled" }
8353
8354 if (map.fallthrough) {
8355 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
8356 { return lookupKey(key, map.fallthrough, handle, context) }
8357 for (var i = 0; i < map.fallthrough.length; i++) {
8358 var result = lookupKey(key, map.fallthrough[i], handle, context);
8359 if (result) { return result }
8360 }
8361 }
8362 }
8363
8364 // Modifier key presses don't count as 'real' key presses for the
8365 // purpose of keymap fallthrough.
8366 function isModifierKey(value) {
8367 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8368 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8369 }
8370
8371 function addModifierNames(name, event, noShift) {
8372 var base = name;
8373 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8374 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8375 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8376 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8377 return name
8378 }
8379
8380 // Look up the name of a key as indicated by an event object.
8381 function keyName(event, noShift) {
8382 if (presto && event.keyCode == 34 && event["char"]) { return false }
8383 var name = keyNames[event.keyCode];
8384 if (name == null || event.altGraphKey) { return false }
8385 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8386 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8387 if (event.keyCode == 3 && event.code) { name = event.code; }
8388 return addModifierNames(name, event, noShift)
8389 }
8390
8391 function getKeyMap(val) {
8392 return typeof val == "string" ? keyMap[val] : val
8393 }
8394
8395 // Helper for deleting text near the selection(s), used to implement
8396 // backspace, delete, and similar functionality.
8397 function deleteNearSelection(cm, compute) {
8398 var ranges = cm.doc.sel.ranges, kill = [];
8399 // Build up a set of ranges to kill first, merging overlapping
8400 // ranges.
8401 for (var i = 0; i < ranges.length; i++) {
8402 var toKill = compute(ranges[i]);
8403 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8404 var replaced = kill.pop();
8405 if (cmp(replaced.from, toKill.from) < 0) {
8406 toKill.from = replaced.from;
8407 break
8408 }
8409 }
8410 kill.push(toKill);
8411 }
8412 // Next, remove those actual ranges.
8413 runInOp(cm, function () {
8414 for (var i = kill.length - 1; i >= 0; i--)
8415 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8416 ensureCursorVisible(cm);
8417 });
8418 }
8419
8420 function moveCharLogically(line, ch, dir) {
8421 var target = skipExtendingChars(line.text, ch + dir, dir);
8422 return target < 0 || target > line.text.length ? null : target
8423 }
8424
8425 function moveLogically(line, start, dir) {
8426 var ch = moveCharLogically(line, start.ch, dir);
8427 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8428 }
8429
8430 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8431 if (visually) {
8432 if (cm.doc.direction == "rtl") { dir = -dir; }
8433 var order = getOrder(lineObj, cm.doc.direction);
8434 if (order) {
8435 var part = dir < 0 ? lst(order) : order[0];
8436 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8437 var sticky = moveInStorageOrder ? "after" : "before";
8438 var ch;
8439 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8440 // it could be that the last bidi part is not on the last visual line,
8441 // since visual lines contain content order-consecutive chunks.
8442 // Thus, in rtl, we are looking for the first (content-order) character
8443 // in the rtl chunk that is on the last line (that is, the same line
8444 // as the last (content-order) character).
8445 if (part.level > 0 || cm.doc.direction == "rtl") {
8446 var prep = prepareMeasureForLine(cm, lineObj);
8447 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8448 var targetTop = measureCharPrepared(cm, prep, ch).top;
8449 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8450 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8451 } else { ch = dir < 0 ? part.to : part.from; }
8452 return new Pos(lineNo, ch, sticky)
8453 }
8454 }
8455 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8456 }
8457
8458 function moveVisually(cm, line, start, dir) {
8459 var bidi = getOrder(line, cm.doc.direction);
8460 if (!bidi) { return moveLogically(line, start, dir) }
8461 if (start.ch >= line.text.length) {
8462 start.ch = line.text.length;
8463 start.sticky = "before";
8464 } else if (start.ch <= 0) {
8465 start.ch = 0;
8466 start.sticky = "after";
8467 }
8468 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8469 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8470 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8471 // nothing interesting happens.
8472 return moveLogically(line, start, dir)
8473 }
8474
8475 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8476 var prep;
8477 var getWrappedLineExtent = function (ch) {
8478 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8479 prep = prep || prepareMeasureForLine(cm, line);
8480 return wrappedLineExtentChar(cm, line, prep, ch)
8481 };
8482 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8483
8484 if (cm.doc.direction == "rtl" || part.level == 1) {
8485 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8486 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8487 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8488 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8489 var sticky = moveInStorageOrder ? "before" : "after";
8490 return new Pos(start.line, ch, sticky)
8491 }
8492 }
8493
8494 // Case 3: Could not move within this bidi part in this visual line, so leave
8495 // the current bidi part
8496
8497 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8498 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8499 ? new Pos(start.line, mv(ch, 1), "before")
8500 : new Pos(start.line, ch, "after"); };
8501
8502 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8503 var part = bidi[partPos];
8504 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8505 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8506 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8507 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8508 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8509 }
8510 };
8511
8512 // Case 3a: Look for other bidi parts on the same visual line
8513 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8514 if (res) { return res }
8515
8516 // Case 3b: Look for other bidi parts on the next visual line
8517 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8518 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8519 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8520 if (res) { return res }
8521 }
8522
8523 // Case 4: Nowhere to move
8524 return null
8525 }
8526
8527 // Commands are parameter-less actions that can be performed on an
8528 // editor, mostly used for keybindings.
8529 var commands = {
8530 selectAll: selectAll,
8531 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8532 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8533 if (range.empty()) {
8534 var len = getLine(cm.doc, range.head.line).text.length;
8535 if (range.head.ch == len && range.head.line < cm.lastLine())
8536 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8537 else
8538 { return {from: range.head, to: Pos(range.head.line, len)} }
8539 } else {
8540 return {from: range.from(), to: range.to()}
8541 }
8542 }); },
8543 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8544 from: Pos(range.from().line, 0),
8545 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8546 }); }); },
8547 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8548 from: Pos(range.from().line, 0), to: range.from()
8549 }); }); },
8550 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8551 var top = cm.charCoords(range.head, "div").top + 5;
8552 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8553 return {from: leftPos, to: range.from()}
8554 }); },
8555 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8556 var top = cm.charCoords(range.head, "div").top + 5;
8557 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8558 return {from: range.from(), to: rightPos }
8559 }); },
8560 undo: function (cm) { return cm.undo(); },
8561 redo: function (cm) { return cm.redo(); },
8562 undoSelection: function (cm) { return cm.undoSelection(); },
8563 redoSelection: function (cm) { return cm.redoSelection(); },
8564 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8565 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8566 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8567 {origin: "+move", bias: 1}
8568 ); },
8569 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8570 {origin: "+move", bias: 1}
8571 ); },
8572 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8573 {origin: "+move", bias: -1}
8574 ); },
8575 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8576 var top = cm.cursorCoords(range.head, "div").top + 5;
8577 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8578 }, sel_move); },
8579 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8580 var top = cm.cursorCoords(range.head, "div").top + 5;
8581 return cm.coordsChar({left: 0, top: top}, "div")
8582 }, sel_move); },
8583 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8584 var top = cm.cursorCoords(range.head, "div").top + 5;
8585 var pos = cm.coordsChar({left: 0, top: top}, "div");
8586 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8587 return pos
8588 }, sel_move); },
8589 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8590 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8591 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8592 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8593 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8594 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8595 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8596 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8597 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8598 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8599 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8600 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8601 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8602 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8603 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8604 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8605 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8606 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8607 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8608 indentMore: function (cm) { return cm.indentSelection("add"); },
8609 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8610 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8611 insertSoftTab: function (cm) {
8612 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8613 for (var i = 0; i < ranges.length; i++) {
8614 var pos = ranges[i].from();
8615 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8616 spaces.push(spaceStr(tabSize - col % tabSize));
8617 }
8618 cm.replaceSelections(spaces);
8619 },
8620 defaultTab: function (cm) {
8621 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8622 else { cm.execCommand("insertTab"); }
8623 },
8624 // Swap the two chars left and right of each selection's head.
8625 // Move cursor behind the two swapped characters afterwards.
8626 //
8627 // Doesn't consider line feeds a character.
8628 // Doesn't scan more than one line above to find a character.
8629 // Doesn't do anything on an empty line.
8630 // Doesn't do anything with non-empty selections.
8631 transposeChars: function (cm) { return runInOp(cm, function () {
8632 var ranges = cm.listSelections(), newSel = [];
8633 for (var i = 0; i < ranges.length; i++) {
8634 if (!ranges[i].empty()) { continue }
8635 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8636 if (line) {
8637 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8638 if (cur.ch > 0) {
8639 cur = new Pos(cur.line, cur.ch + 1);
8640 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8641 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8642 } else if (cur.line > cm.doc.first) {
8643 var prev = getLine(cm.doc, cur.line - 1).text;
8644 if (prev) {
8645 cur = new Pos(cur.line, 1);
8646 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8647 prev.charAt(prev.length - 1),
8648 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8649 }
8650 }
8651 }
8652 newSel.push(new Range(cur, cur));
8653 }
8654 cm.setSelections(newSel);
8655 }); },
8656 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8657 var sels = cm.listSelections();
8658 for (var i = sels.length - 1; i >= 0; i--)
8659 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8660 sels = cm.listSelections();
8661 for (var i$1 = 0; i$1 < sels.length; i$1++)
8662 { cm.indentLine(sels[i$1].from().line, null, true); }
8663 ensureCursorVisible(cm);
8664 }); },
8665 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8666 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8667 };
8668
8669
8670 function lineStart(cm, lineN) {
8671 var line = getLine(cm.doc, lineN);
8672 var visual = visualLine(line);
8673 if (visual != line) { lineN = lineNo(visual); }
8674 return endOfLine(true, cm, visual, lineN, 1)
8675 }
8676 function lineEnd(cm, lineN) {
8677 var line = getLine(cm.doc, lineN);
8678 var visual = visualLineEnd(line);
8679 if (visual != line) { lineN = lineNo(visual); }
8680 return endOfLine(true, cm, line, lineN, -1)
8681 }
8682 function lineStartSmart(cm, pos) {
8683 var start = lineStart(cm, pos.line);
8684 var line = getLine(cm.doc, start.line);
8685 var order = getOrder(line, cm.doc.direction);
8686 if (!order || order[0].level == 0) {
8687 var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
8688 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8689 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8690 }
8691 return start
8692 }
8693
8694 // Run a handler that was bound to a key.
8695 function doHandleBinding(cm, bound, dropShift) {
8696 if (typeof bound == "string") {
8697 bound = commands[bound];
8698 if (!bound) { return false }
8699 }
8700 // Ensure previous input has been read, so that the handler sees a
8701 // consistent view of the document
8702 cm.display.input.ensurePolled();
8703 var prevShift = cm.display.shift, done = false;
8704 try {
8705 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8706 if (dropShift) { cm.display.shift = false; }
8707 done = bound(cm) != Pass;
8708 } finally {
8709 cm.display.shift = prevShift;
8710 cm.state.suppressEdits = false;
8711 }
8712 return done
8713 }
8714
8715 function lookupKeyForEditor(cm, name, handle) {
8716 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8717 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8718 if (result) { return result }
8719 }
8720 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8721 || lookupKey(name, cm.options.keyMap, handle, cm)
8722 }
8723
8724 // Note that, despite the name, this function is also used to check
8725 // for bound mouse clicks.
8726
8727 var stopSeq = new Delayed;
8728
8729 function dispatchKey(cm, name, e, handle) {
8730 var seq = cm.state.keySeq;
8731 if (seq) {
8732 if (isModifierKey(name)) { return "handled" }
8733 if (/\'$/.test(name))
8734 { cm.state.keySeq = null; }
8735 else
8736 { stopSeq.set(50, function () {
8737 if (cm.state.keySeq == seq) {
8738 cm.state.keySeq = null;
8739 cm.display.input.reset();
8740 }
8741 }); }
8742 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8743 }
8744 return dispatchKeyInner(cm, name, e, handle)
8745 }
8746
8747 function dispatchKeyInner(cm, name, e, handle) {
8748 var result = lookupKeyForEditor(cm, name, handle);
8749
8750 if (result == "multi")
8751 { cm.state.keySeq = name; }
8752 if (result == "handled")
8753 { signalLater(cm, "keyHandled", cm, name, e); }
8754
8755 if (result == "handled" || result == "multi") {
8756 e_preventDefault(e);
8757 restartBlink(cm);
8758 }
8759
8760 return !!result
8761 }
8762
8763 // Handle a key from the keydown event.
8764 function handleKeyBinding(cm, e) {
8765 var name = keyName(e, true);
8766 if (!name) { return false }
8767
8768 if (e.shiftKey && !cm.state.keySeq) {
8769 // First try to resolve full name (including 'Shift-'). Failing
8770 // that, see if there is a cursor-motion command (starting with
8771 // 'go') bound to the keyname without 'Shift-'.
8772 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8773 || dispatchKey(cm, name, e, function (b) {
8774 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8775 { return doHandleBinding(cm, b) }
8776 })
8777 } else {
8778 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8779 }
8780 }
8781
8782 // Handle a key from the keypress event
8783 function handleCharBinding(cm, e, ch) {
8784 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8785 }
8786
8787 var lastStoppedKey = null;
8788 function onKeyDown(e) {
8789 var cm = this;
8790 cm.curOp.focus = activeElt();
8791 if (signalDOMEvent(cm, e)) { return }
8792 // IE does strange things with escape.
8793 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8794 var code = e.keyCode;
8795 cm.display.shift = code == 16 || e.shiftKey;
8796 var handled = handleKeyBinding(cm, e);
8797 if (presto) {
8798 lastStoppedKey = handled ? code : null;
8799 // Opera has no cut event... we try to at least catch the key combo
8800 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8801 { cm.replaceSelection("", null, "cut"); }
8802 }
8803 if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
8804 { document.execCommand("cut"); }
8805
8806 // Turn mouse into crosshair when Alt is held on Mac.
8807 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8808 { showCrossHair(cm); }
8809 }
8810
8811 function showCrossHair(cm) {
8812 var lineDiv = cm.display.lineDiv;
8813 addClass(lineDiv, "CodeMirror-crosshair");
8814
8815 function up(e) {
8816 if (e.keyCode == 18 || !e.altKey) {
8817 rmClass(lineDiv, "CodeMirror-crosshair");
8818 off(document, "keyup", up);
8819 off(document, "mouseover", up);
8820 }
8821 }
8822 on(document, "keyup", up);
8823 on(document, "mouseover", up);
8824 }
8825
8826 function onKeyUp(e) {
8827 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8828 signalDOMEvent(this, e);
8829 }
8830
8831 function onKeyPress(e) {
8832 var cm = this;
8833 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8834 var keyCode = e.keyCode, charCode = e.charCode;
8835 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8836 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8837 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8838 // Some browsers fire keypress events for backspace
8839 if (ch == "\x08") { return }
8840 if (handleCharBinding(cm, e, ch)) { return }
8841 cm.display.input.onKeyPress(e);
8842 }
8843
8844 var DOUBLECLICK_DELAY = 400;
8845
8846 var PastClick = function(time, pos, button) {
8847 this.time = time;
8848 this.pos = pos;
8849 this.button = button;
8850 };
8851
8852 PastClick.prototype.compare = function (time, pos, button) {
8853 return this.time + DOUBLECLICK_DELAY > time &&
8854 cmp(pos, this.pos) == 0 && button == this.button
8855 };
8856
8857 var lastClick, lastDoubleClick;
8858 function clickRepeat(pos, button) {
8859 var now = +new Date;
8860 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8861 lastClick = lastDoubleClick = null;
8862 return "triple"
8863 } else if (lastClick && lastClick.compare(now, pos, button)) {
8864 lastDoubleClick = new PastClick(now, pos, button);
8865 lastClick = null;
8866 return "double"
8867 } else {
8868 lastClick = new PastClick(now, pos, button);
8869 lastDoubleClick = null;
8870 return "single"
8871 }
8872 }
8873
8874 // A mouse down can be a single click, double click, triple click,
8875 // start of selection drag, start of text drag, new cursor
8876 // (ctrl-click), rectangle drag (alt-drag), or xwin
8877 // middle-click-paste. Or it might be a click on something we should
8878 // not interfere with, such as a scrollbar or widget.
8879 function onMouseDown(e) {
8880 var cm = this, display = cm.display;
8881 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8882 display.input.ensurePolled();
8883 display.shift = e.shiftKey;
8884
8885 if (eventInWidget(display, e)) {
8886 if (!webkit) {
8887 // Briefly turn off draggability, to allow widgets to do
8888 // normal dragging things.
8889 display.scroller.draggable = false;
8890 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8891 }
8892 return
8893 }
8894 if (clickInGutter(cm, e)) { return }
8895 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8896 window.focus();
8897
8898 // #3261: make sure, that we're not starting a second selection
8899 if (button == 1 && cm.state.selectingText)
8900 { cm.state.selectingText(e); }
8901
8902 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8903
8904 if (button == 1) {
8905 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8906 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8907 } else if (button == 2) {
8908 if (pos) { extendSelection(cm.doc, pos); }
8909 setTimeout(function () { return display.input.focus(); }, 20);
8910 } else if (button == 3) {
8911 if (captureRightClick) { cm.display.input.onContextMenu(e); }
8912 else { delayBlurEvent(cm); }
8913 }
8914 }
8915
8916 function handleMappedButton(cm, button, pos, repeat, event) {
8917 var name = "Click";
8918 if (repeat == "double") { name = "Double" + name; }
8919 else if (repeat == "triple") { name = "Triple" + name; }
8920 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8921
8922 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8923 if (typeof bound == "string") { bound = commands[bound]; }
8924 if (!bound) { return false }
8925 var done = false;
8926 try {
8927 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8928 done = bound(cm, pos) != Pass;
8929 } finally {
8930 cm.state.suppressEdits = false;
8931 }
8932 return done
8933 })
8934 }
8935
8936 function configureMouse(cm, repeat, event) {
8937 var option = cm.getOption("configureMouse");
8938 var value = option ? option(cm, repeat, event) : {};
8939 if (value.unit == null) {
8940 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8941 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8942 }
8943 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8944 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8945 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8946 return value
8947 }
8948
8949 function leftButtonDown(cm, pos, repeat, event) {
8950 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8951 else { cm.curOp.focus = activeElt(); }
8952
8953 var behavior = configureMouse(cm, repeat, event);
8954
8955 var sel = cm.doc.sel, contained;
8956 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8957 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8958 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8959 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8960 { leftButtonStartDrag(cm, event, pos, behavior); }
8961 else
8962 { leftButtonSelect(cm, event, pos, behavior); }
8963 }
8964
8965 // Start a text drag. When it ends, see if any dragging actually
8966 // happen, and treat as a click if it didn't.
8967 function leftButtonStartDrag(cm, event, pos, behavior) {
8968 var display = cm.display, moved = false;
8969 var dragEnd = operation(cm, function (e) {
8970 if (webkit) { display.scroller.draggable = false; }
8971 cm.state.draggingText = false;
8972 off(display.wrapper.ownerDocument, "mouseup", dragEnd);
8973 off(display.wrapper.ownerDocument, "mousemove", mouseMove);
8974 off(display.scroller, "dragstart", dragStart);
8975 off(display.scroller, "drop", dragEnd);
8976 if (!moved) {
8977 e_preventDefault(e);
8978 if (!behavior.addNew)
8979 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8980 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8981 if (webkit || ie && ie_version == 9)
8982 { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
8983 else
8984 { display.input.focus(); }
8985 }
8986 });
8987 var mouseMove = function(e2) {
8988 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8989 };
8990 var dragStart = function () { return moved = true; };
8991 // Let the drag handler handle this.
8992 if (webkit) { display.scroller.draggable = true; }
8993 cm.state.draggingText = dragEnd;
8994 dragEnd.copy = !behavior.moveOnDrag;
8995 // IE's approach to draggable
8996 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8997 on(display.wrapper.ownerDocument, "mouseup", dragEnd);
8998 on(display.wrapper.ownerDocument, "mousemove", mouseMove);
8999 on(display.scroller, "dragstart", dragStart);
9000 on(display.scroller, "drop", dragEnd);
9001
9002 delayBlurEvent(cm);
9003 setTimeout(function () { return display.input.focus(); }, 20);
9004 }
9005
9006 function rangeForUnit(cm, pos, unit) {
9007 if (unit == "char") { return new Range(pos, pos) }
9008 if (unit == "word") { return cm.findWordAt(pos) }
9009 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
9010 var result = unit(cm, pos);
9011 return new Range(result.from, result.to)
9012 }
9013
9014 // Normal selection, as opposed to text dragging.
9015 function leftButtonSelect(cm, event, start, behavior) {
9016 var display = cm.display, doc = cm.doc;
9017 e_preventDefault(event);
9018
9019 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
9020 if (behavior.addNew && !behavior.extend) {
9021 ourIndex = doc.sel.contains(start);
9022 if (ourIndex > -1)
9023 { ourRange = ranges[ourIndex]; }
9024 else
9025 { ourRange = new Range(start, start); }
9026 } else {
9027 ourRange = doc.sel.primary();
9028 ourIndex = doc.sel.primIndex;
9029 }
9030
9031 if (behavior.unit == "rectangle") {
9032 if (!behavior.addNew) { ourRange = new Range(start, start); }
9033 start = posFromMouse(cm, event, true, true);
9034 ourIndex = -1;
9035 } else {
9036 var range = rangeForUnit(cm, start, behavior.unit);
9037 if (behavior.extend)
9038 { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }
9039 else
9040 { ourRange = range; }
9041 }
9042
9043 if (!behavior.addNew) {
9044 ourIndex = 0;
9045 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
9046 startSel = doc.sel;
9047 } else if (ourIndex == -1) {
9048 ourIndex = ranges.length;
9049 setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
9050 {scroll: false, origin: "*mouse"});
9051 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
9052 setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
9053 {scroll: false, origin: "*mouse"});
9054 startSel = doc.sel;
9055 } else {
9056 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
9057 }
9058
9059 var lastPos = start;
9060 function extendTo(pos) {
9061 if (cmp(lastPos, pos) == 0) { return }
9062 lastPos = pos;
9063
9064 if (behavior.unit == "rectangle") {
9065 var ranges = [], tabSize = cm.options.tabSize;
9066 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
9067 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
9068 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
9069 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
9070 line <= end; line++) {
9071 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
9072 if (left == right)
9073 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
9074 else if (text.length > leftPos)
9075 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
9076 }
9077 if (!ranges.length) { ranges.push(new Range(start, start)); }
9078 setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
9079 {origin: "*mouse", scroll: false});
9080 cm.scrollIntoView(pos);
9081 } else {
9082 var oldRange = ourRange;
9083 var range = rangeForUnit(cm, pos, behavior.unit);
9084 var anchor = oldRange.anchor, head;
9085 if (cmp(range.anchor, anchor) > 0) {
9086 head = range.head;
9087 anchor = minPos(oldRange.from(), range.anchor);
9088 } else {
9089 head = range.anchor;
9090 anchor = maxPos(oldRange.to(), range.head);
9091 }
9092 var ranges$1 = startSel.ranges.slice(0);
9093 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
9094 setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
9095 }
9096 }
9097
9098 var editorSize = display.wrapper.getBoundingClientRect();
9099 // Used to ensure timeout re-tries don't fire when another extend
9100 // happened in the meantime (clearTimeout isn't reliable -- at
9101 // least on Chrome, the timeouts still happen even when cleared,
9102 // if the clear happens after their scheduled firing time).
9103 var counter = 0;
9104
9105 function extend(e) {
9106 var curCount = ++counter;
9107 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
9108 if (!cur) { return }
9109 if (cmp(cur, lastPos) != 0) {
9110 cm.curOp.focus = activeElt();
9111 extendTo(cur);
9112 var visible = visibleLines(display, doc);
9113 if (cur.line >= visible.to || cur.line < visible.from)
9114 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
9115 } else {
9116 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
9117 if (outside) { setTimeout(operation(cm, function () {
9118 if (counter != curCount) { return }
9119 display.scroller.scrollTop += outside;
9120 extend(e);
9121 }), 50); }
9122 }
9123 }
9124
9125 function done(e) {
9126 cm.state.selectingText = false;
9127 counter = Infinity;
9128 // If e is null or undefined we interpret this as someone trying
9129 // to explicitly cancel the selection rather than the user
9130 // letting go of the mouse button.
9131 if (e) {
9132 e_preventDefault(e);
9133 display.input.focus();
9134 }
9135 off(display.wrapper.ownerDocument, "mousemove", move);
9136 off(display.wrapper.ownerDocument, "mouseup", up);
9137 doc.history.lastSelOrigin = null;
9138 }
9139
9140 var move = operation(cm, function (e) {
9141 if (e.buttons === 0 || !e_button(e)) { done(e); }
9142 else { extend(e); }
9143 });
9144 var up = operation(cm, done);
9145 cm.state.selectingText = up;
9146 on(display.wrapper.ownerDocument, "mousemove", move);
9147 on(display.wrapper.ownerDocument, "mouseup", up);
9148 }
9149
9150 // Used when mouse-selecting to adjust the anchor to the proper side
9151 // of a bidi jump depending on the visual position of the head.
9152 function bidiSimplify(cm, range) {
9153 var anchor = range.anchor;
9154 var head = range.head;
9155 var anchorLine = getLine(cm.doc, anchor.line);
9156 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
9157 var order = getOrder(anchorLine);
9158 if (!order) { return range }
9159 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
9160 if (part.from != anchor.ch && part.to != anchor.ch) { return range }
9161 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
9162 if (boundary == 0 || boundary == order.length) { return range }
9163
9164 // Compute the relative visual position of the head compared to the
9165 // anchor (<0 is to the left, >0 to the right)
9166 var leftSide;
9167 if (head.line != anchor.line) {
9168 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
9169 } else {
9170 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
9171 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
9172 if (headIndex == boundary - 1 || headIndex == boundary)
9173 { leftSide = dir < 0; }
9174 else
9175 { leftSide = dir > 0; }
9176 }
9177
9178 var usePart = order[boundary + (leftSide ? -1 : 0)];
9179 var from = leftSide == (usePart.level == 1);
9180 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
9181 return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
9182 }
9183
9184
9185 // Determines whether an event happened in the gutter, and fires the
9186 // handlers for the corresponding event.
9187 function gutterEvent(cm, e, type, prevent) {
9188 var mX, mY;
9189 if (e.touches) {
9190 mX = e.touches[0].clientX;
9191 mY = e.touches[0].clientY;
9192 } else {
9193 try { mX = e.clientX; mY = e.clientY; }
9194 catch(e) { return false }
9195 }
9196 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9197 if (prevent) { e_preventDefault(e); }
9198
9199 var display = cm.display;
9200 var lineBox = display.lineDiv.getBoundingClientRect();
9201
9202 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9203 mY -= lineBox.top - display.viewOffset;
9204
9205 for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
9206 var g = display.gutters.childNodes[i];
9207 if (g && g.getBoundingClientRect().right >= mX) {
9208 var line = lineAtHeight(cm.doc, mY);
9209 var gutter = cm.display.gutterSpecs[i];
9210 signal(cm, type, cm, line, gutter.className, e);
9211 return e_defaultPrevented(e)
9212 }
9213 }
9214 }
9215
9216 function clickInGutter(cm, e) {
9217 return gutterEvent(cm, e, "gutterClick", true)
9218 }
9219
9220 // CONTEXT MENU HANDLING
9221
9222 // To make the context menu work, we need to briefly unhide the
9223 // textarea (making it as unobtrusive as possible) to let the
9224 // right-click take effect on it.
9225 function onContextMenu(cm, e) {
9226 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9227 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9228 if (!captureRightClick) { cm.display.input.onContextMenu(e); }
9229 }
9230
9231 function contextMenuInGutter(cm, e) {
9232 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9233 return gutterEvent(cm, e, "gutterContextMenu", false)
9234 }
9235
9236 function themeChanged(cm) {
9237 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9238 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9239 clearCaches(cm);
9240 }
9241
9242 var Init = {toString: function(){return "CodeMirror.Init"}};
9243
9244 var defaults = {};
9245 var optionHandlers = {};
9246
9247 function defineOptions(CodeMirror) {
9248 var optionHandlers = CodeMirror.optionHandlers;
9249
9250 function option(name, deflt, handle, notOnInit) {
9251 CodeMirror.defaults[name] = deflt;
9252 if (handle) { optionHandlers[name] =
9253 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9254 }
9255
9256 CodeMirror.defineOption = option;
9257
9258 // Passed to option handlers when there is no old value.
9259 CodeMirror.Init = Init;
9260
9261 // These two are, on init, called from the constructor because they
9262 // have to be initialized before the editor can start at all.
9263 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9264 option("mode", null, function (cm, val) {
9265 cm.doc.modeOption = val;
9266 loadMode(cm);
9267 }, true);
9268
9269 option("indentUnit", 2, loadMode, true);
9270 option("indentWithTabs", false);
9271 option("smartIndent", true);
9272 option("tabSize", 4, function (cm) {
9273 resetModeState(cm);
9274 clearCaches(cm);
9275 regChange(cm);
9276 }, true);
9277
9278 option("lineSeparator", null, function (cm, val) {
9279 cm.doc.lineSep = val;
9280 if (!val) { return }
9281 var newBreaks = [], lineNo = cm.doc.first;
9282 cm.doc.iter(function (line) {
9283 for (var pos = 0;;) {
9284 var found = line.text.indexOf(val, pos);
9285 if (found == -1) { break }
9286 pos = found + val.length;
9287 newBreaks.push(Pos(lineNo, found));
9288 }
9289 lineNo++;
9290 });
9291 for (var i = newBreaks.length - 1; i >= 0; i--)
9292 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9293 });
9294 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
9295 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9296 if (old != Init) { cm.refresh(); }
9297 });
9298 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9299 option("electricChars", true);
9300 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9301 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9302 }, true);
9303 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9304 option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
9305 option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
9306 option("rtlMoveVisually", !windows);
9307 option("wholeLineUpdateBefore", true);
9308
9309 option("theme", "default", function (cm) {
9310 themeChanged(cm);
9311 updateGutters(cm);
9312 }, true);
9313 option("keyMap", "default", function (cm, val, old) {
9314 var next = getKeyMap(val);
9315 var prev = old != Init && getKeyMap(old);
9316 if (prev && prev.detach) { prev.detach(cm, next); }
9317 if (next.attach) { next.attach(cm, prev || null); }
9318 });
9319 option("extraKeys", null);
9320 option("configureMouse", null);
9321
9322 option("lineWrapping", false, wrappingChanged, true);
9323 option("gutters", [], function (cm, val) {
9324 cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
9325 updateGutters(cm);
9326 }, true);
9327 option("fixedGutter", true, function (cm, val) {
9328 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9329 cm.refresh();
9330 }, true);
9331 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9332 option("scrollbarStyle", "native", function (cm) {
9333 initScrollbars(cm);
9334 updateScrollbars(cm);
9335 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9336 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9337 }, true);
9338 option("lineNumbers", false, function (cm, val) {
9339 cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
9340 updateGutters(cm);
9341 }, true);
9342 option("firstLineNumber", 1, updateGutters, true);
9343 option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
9344 option("showCursorWhenSelecting", false, updateSelection, true);
9345
9346 option("resetSelectionOnContextMenu", true);
9347 option("lineWiseCopyCut", true);
9348 option("pasteLinesPerSelection", true);
9349 option("selectionsMayTouch", false);
9350
9351 option("readOnly", false, function (cm, val) {
9352 if (val == "nocursor") {
9353 onBlur(cm);
9354 cm.display.input.blur();
9355 }
9356 cm.display.input.readOnlyChanged(val);
9357 });
9358
9359 option("screenReaderLabel", null, function (cm, val) {
9360 val = (val === '') ? null : val;
9361 cm.display.input.screenReaderLabelChanged(val);
9362 });
9363
9364 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9365 option("dragDrop", true, dragDropChanged);
9366 option("allowDropFileTypes", null);
9367
9368 option("cursorBlinkRate", 530);
9369 option("cursorScrollMargin", 0);
9370 option("cursorHeight", 1, updateSelection, true);
9371 option("singleCursorHeightPerLine", true, updateSelection, true);
9372 option("workTime", 100);
9373 option("workDelay", 100);
9374 option("flattenSpans", true, resetModeState, true);
9375 option("addModeClass", false, resetModeState, true);
9376 option("pollInterval", 100);
9377 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9378 option("historyEventDelay", 1250);
9379 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9380 option("maxHighlightLength", 10000, resetModeState, true);
9381 option("moveInputWithCursor", true, function (cm, val) {
9382 if (!val) { cm.display.input.resetPosition(); }
9383 });
9384
9385 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9386 option("autofocus", null);
9387 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9388 option("phrases", null);
9389 }
9390
9391 function dragDropChanged(cm, value, old) {
9392 var wasOn = old && old != Init;
9393 if (!value != !wasOn) {
9394 var funcs = cm.display.dragFunctions;
9395 var toggle = value ? on : off;
9396 toggle(cm.display.scroller, "dragstart", funcs.start);
9397 toggle(cm.display.scroller, "dragenter", funcs.enter);
9398 toggle(cm.display.scroller, "dragover", funcs.over);
9399 toggle(cm.display.scroller, "dragleave", funcs.leave);
9400 toggle(cm.display.scroller, "drop", funcs.drop);
9401 }
9402 }
9403
9404 function wrappingChanged(cm) {
9405 if (cm.options.lineWrapping) {
9406 addClass(cm.display.wrapper, "CodeMirror-wrap");
9407 cm.display.sizer.style.minWidth = "";
9408 cm.display.sizerWidth = null;
9409 } else {
9410 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9411 findMaxLine(cm);
9412 }
9413 estimateLineHeights(cm);
9414 regChange(cm);
9415 clearCaches(cm);
9416 setTimeout(function () { return updateScrollbars(cm); }, 100);
9417 }
9418
9419 // A CodeMirror instance represents an editor. This is the object
9420 // that user code is usually dealing with.
9421
9422 function CodeMirror(place, options) {
9423 var this$1 = this;
9424
9425 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
9426
9427 this.options = options = options ? copyObj(options) : {};
9428 // Determine effective options based on given values and defaults.
9429 copyObj(defaults, options, false);
9430
9431 var doc = options.value;
9432 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9433 else if (options.mode) { doc.modeOption = options.mode; }
9434 this.doc = doc;
9435
9436 var input = new CodeMirror.inputStyles[options.inputStyle](this);
9437 var display = this.display = new Display(place, doc, input, options);
9438 display.wrapper.CodeMirror = this;
9439 themeChanged(this);
9440 if (options.lineWrapping)
9441 { this.display.wrapper.className += " CodeMirror-wrap"; }
9442 initScrollbars(this);
9443
9444 this.state = {
9445 keyMaps: [], // stores maps added by addKeyMap
9446 overlays: [], // highlighting overlays, as added by addOverlay
9447 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9448 overwrite: false,
9449 delayingBlurEvent: false,
9450 focused: false,
9451 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9452 pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
9453 selectingText: false,
9454 draggingText: false,
9455 highlight: new Delayed(), // stores highlight worker timeout
9456 keySeq: null, // Unfinished key sequence
9457 specialChars: null
9458 };
9459
9460 if (options.autofocus && !mobile) { display.input.focus(); }
9461
9462 // Override magic textarea content restore that IE sometimes does
9463 // on our hidden textarea on reload
9464 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9465
9466 registerEventHandlers(this);
9467 ensureGlobalHandlers();
9468
9469 startOperation(this);
9470 this.curOp.forceUpdate = true;
9471 attachDoc(this, doc);
9472
9473 if ((options.autofocus && !mobile) || this.hasFocus())
9474 { setTimeout(bind(onFocus, this), 20); }
9475 else
9476 { onBlur(this); }
9477
9478 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9479 { optionHandlers[opt](this, options[opt], Init); } }
9480 maybeUpdateLineNumberWidth(this);
9481 if (options.finishInit) { options.finishInit(this); }
9482 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }
9483 endOperation(this);
9484 // Suppress optimizelegibility in Webkit, since it breaks text
9485 // measuring on line wrapping boundaries.
9486 if (webkit && options.lineWrapping &&
9487 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9488 { display.lineDiv.style.textRendering = "auto"; }
9489 }
9490
9491 // The default configuration options.
9492 CodeMirror.defaults = defaults;
9493 // Functions to run when options are changed.
9494 CodeMirror.optionHandlers = optionHandlers;
9495
9496 // Attach the necessary event handlers when initializing the editor
9497 function registerEventHandlers(cm) {
9498 var d = cm.display;
9499 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9500 // Older IE's will not fire a second mousedown for a double click
9501 if (ie && ie_version < 11)
9502 { on(d.scroller, "dblclick", operation(cm, function (e) {
9503 if (signalDOMEvent(cm, e)) { return }
9504 var pos = posFromMouse(cm, e);
9505 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9506 e_preventDefault(e);
9507 var word = cm.findWordAt(pos);
9508 extendSelection(cm.doc, word.anchor, word.head);
9509 })); }
9510 else
9511 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9512 // Some browsers fire contextmenu *after* opening the menu, at
9513 // which point we can't mess with it anymore. Context menu is
9514 // handled in onMouseDown for these browsers.
9515 on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
9516 on(d.input.getField(), "contextmenu", function (e) {
9517 if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
9518 });
9519
9520 // Used to suppress mouse event handling when a touch happens
9521 var touchFinished, prevTouch = {end: 0};
9522 function finishTouch() {
9523 if (d.activeTouch) {
9524 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9525 prevTouch = d.activeTouch;
9526 prevTouch.end = +new Date;
9527 }
9528 }
9529 function isMouseLikeTouchEvent(e) {
9530 if (e.touches.length != 1) { return false }
9531 var touch = e.touches[0];
9532 return touch.radiusX <= 1 && touch.radiusY <= 1
9533 }
9534 function farAway(touch, other) {
9535 if (other.left == null) { return true }
9536 var dx = other.left - touch.left, dy = other.top - touch.top;
9537 return dx * dx + dy * dy > 20 * 20
9538 }
9539 on(d.scroller, "touchstart", function (e) {
9540 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9541 d.input.ensurePolled();
9542 clearTimeout(touchFinished);
9543 var now = +new Date;
9544 d.activeTouch = {start: now, moved: false,
9545 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9546 if (e.touches.length == 1) {
9547 d.activeTouch.left = e.touches[0].pageX;
9548 d.activeTouch.top = e.touches[0].pageY;
9549 }
9550 }
9551 });
9552 on(d.scroller, "touchmove", function () {
9553 if (d.activeTouch) { d.activeTouch.moved = true; }
9554 });
9555 on(d.scroller, "touchend", function (e) {
9556 var touch = d.activeTouch;
9557 if (touch && !eventInWidget(d, e) && touch.left != null &&
9558 !touch.moved && new Date - touch.start < 300) {
9559 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9560 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9561 { range = new Range(pos, pos); }
9562 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9563 { range = cm.findWordAt(pos); }
9564 else // Triple tap
9565 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9566 cm.setSelection(range.anchor, range.head);
9567 cm.focus();
9568 e_preventDefault(e);
9569 }
9570 finishTouch();
9571 });
9572 on(d.scroller, "touchcancel", finishTouch);
9573
9574 // Sync scrolling between fake scrollbars and real scrollable
9575 // area, ensure viewport is updated when scrolling.
9576 on(d.scroller, "scroll", function () {
9577 if (d.scroller.clientHeight) {
9578 updateScrollTop(cm, d.scroller.scrollTop);
9579 setScrollLeft(cm, d.scroller.scrollLeft, true);
9580 signal(cm, "scroll", cm);
9581 }
9582 });
9583
9584 // Listen to wheel events in order to try and update the viewport on time.
9585 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9586 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9587
9588 // Prevent wrapper from ever scrolling
9589 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9590
9591 d.dragFunctions = {
9592 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9593 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9594 start: function (e) { return onDragStart(cm, e); },
9595 drop: operation(cm, onDrop),
9596 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9597 };
9598
9599 var inp = d.input.getField();
9600 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9601 on(inp, "keydown", operation(cm, onKeyDown));
9602 on(inp, "keypress", operation(cm, onKeyPress));
9603 on(inp, "focus", function (e) { return onFocus(cm, e); });
9604 on(inp, "blur", function (e) { return onBlur(cm, e); });
9605 }
9606
9607 var initHooks = [];
9608 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
9609
9610 // Indent the given line. The how parameter can be "smart",
9611 // "add"/null, "subtract", or "prev". When aggressive is false
9612 // (typically set to true for forced single-line indents), empty
9613 // lines are not indented, and places where the mode returns Pass
9614 // are left alone.
9615 function indentLine(cm, n, how, aggressive) {
9616 var doc = cm.doc, state;
9617 if (how == null) { how = "add"; }
9618 if (how == "smart") {
9619 // Fall back to "prev" when the mode doesn't have an indentation
9620 // method.
9621 if (!doc.mode.indent) { how = "prev"; }
9622 else { state = getContextBefore(cm, n).state; }
9623 }
9624
9625 var tabSize = cm.options.tabSize;
9626 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9627 if (line.stateAfter) { line.stateAfter = null; }
9628 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9629 if (!aggressive && !/\S/.test(line.text)) {
9630 indentation = 0;
9631 how = "not";
9632 } else if (how == "smart") {
9633 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9634 if (indentation == Pass || indentation > 150) {
9635 if (!aggressive) { return }
9636 how = "prev";
9637 }
9638 }
9639 if (how == "prev") {
9640 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9641 else { indentation = 0; }
9642 } else if (how == "add") {
9643 indentation = curSpace + cm.options.indentUnit;
9644 } else if (how == "subtract") {
9645 indentation = curSpace - cm.options.indentUnit;
9646 } else if (typeof how == "number") {
9647 indentation = curSpace + how;
9648 }
9649 indentation = Math.max(0, indentation);
9650
9651 var indentString = "", pos = 0;
9652 if (cm.options.indentWithTabs)
9653 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9654 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9655
9656 if (indentString != curSpaceString) {
9657 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9658 line.stateAfter = null;
9659 return true
9660 } else {
9661 // Ensure that, if the cursor was in the whitespace at the start
9662 // of the line, it is moved to the end of that space.
9663 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9664 var range = doc.sel.ranges[i$1];
9665 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9666 var pos$1 = Pos(n, curSpaceString.length);
9667 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9668 break
9669 }
9670 }
9671 }
9672 }
9673
9674 // This will be set to a {lineWise: bool, text: [string]} object, so
9675 // that, when pasting, we know what kind of selections the copied
9676 // text was made out of.
9677 var lastCopied = null;
9678
9679 function setLastCopied(newLastCopied) {
9680 lastCopied = newLastCopied;
9681 }
9682
9683 function applyTextInput(cm, inserted, deleted, sel, origin) {
9684 var doc = cm.doc;
9685 cm.display.shift = false;
9686 if (!sel) { sel = doc.sel; }
9687
9688 var recent = +new Date - 200;
9689 var paste = origin == "paste" || cm.state.pasteIncoming > recent;
9690 var textLines = splitLinesAuto(inserted), multiPaste = null;
9691 // When pasting N lines into N selections, insert one line per selection
9692 if (paste && sel.ranges.length > 1) {
9693 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9694 if (sel.ranges.length % lastCopied.text.length == 0) {
9695 multiPaste = [];
9696 for (var i = 0; i < lastCopied.text.length; i++)
9697 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9698 }
9699 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9700 multiPaste = map(textLines, function (l) { return [l]; });
9701 }
9702 }
9703
9704 var updateInput = cm.curOp.updateInput;
9705 // Normal behavior is to insert the new text into every selection
9706 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9707 var range = sel.ranges[i$1];
9708 var from = range.from(), to = range.to();
9709 if (range.empty()) {
9710 if (deleted && deleted > 0) // Handle deletion
9711 { from = Pos(from.line, from.ch - deleted); }
9712 else if (cm.state.overwrite && !paste) // Handle overwrite
9713 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9714 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9715 { from = to = Pos(from.line, 0); }
9716 }
9717 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9718 origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
9719 makeChange(cm.doc, changeEvent);
9720 signalLater(cm, "inputRead", cm, changeEvent);
9721 }
9722 if (inserted && !paste)
9723 { triggerElectric(cm, inserted); }
9724
9725 ensureCursorVisible(cm);
9726 if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
9727 cm.curOp.typing = true;
9728 cm.state.pasteIncoming = cm.state.cutIncoming = -1;
9729 }
9730
9731 function handlePaste(e, cm) {
9732 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9733 if (pasted) {
9734 e.preventDefault();
9735 if (!cm.isReadOnly() && !cm.options.disableInput)
9736 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9737 return true
9738 }
9739 }
9740
9741 function triggerElectric(cm, inserted) {
9742 // When an 'electric' character is inserted, immediately trigger a reindent
9743 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9744 var sel = cm.doc.sel;
9745
9746 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9747 var range = sel.ranges[i];
9748 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
9749 var mode = cm.getModeAt(range.head);
9750 var indented = false;
9751 if (mode.electricChars) {
9752 for (var j = 0; j < mode.electricChars.length; j++)
9753 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9754 indented = indentLine(cm, range.head.line, "smart");
9755 break
9756 } }
9757 } else if (mode.electricInput) {
9758 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
9759 { indented = indentLine(cm, range.head.line, "smart"); }
9760 }
9761 if (indented) { signalLater(cm, "electricInput", cm, range.head.line); }
9762 }
9763 }
9764
9765 function copyableRanges(cm) {
9766 var text = [], ranges = [];
9767 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9768 var line = cm.doc.sel.ranges[i].head.line;
9769 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9770 ranges.push(lineRange);
9771 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9772 }
9773 return {text: text, ranges: ranges}
9774 }
9775
9776 function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
9777 field.setAttribute("autocorrect", autocorrect ? "" : "off");
9778 field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
9779 field.setAttribute("spellcheck", !!spellcheck);
9780 }
9781
9782 function hiddenTextarea() {
9783 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9784 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9785 // The textarea is kept positioned near the cursor to prevent the
9786 // fact that it'll be scrolled into view on input from scrolling
9787 // our fake cursor out of view. On webkit, when wrap=off, paste is
9788 // very slow. So make the area wide instead.
9789 if (webkit) { te.style.width = "1000px"; }
9790 else { te.setAttribute("wrap", "off"); }
9791 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9792 if (ios) { te.style.border = "1px solid black"; }
9793 disableBrowserMagic(te);
9794 return div
9795 }
9796
9797 // The publicly visible API. Note that methodOp(f) means
9798 // 'wrap f in an operation, performed on its `this` parameter'.
9799
9800 // This is not the complete set of editor methods. Most of the
9801 // methods defined on the Doc type are also injected into
9802 // CodeMirror.prototype, for backwards compatibility and
9803 // convenience.
9804
9805 function addEditorMethods(CodeMirror) {
9806 var optionHandlers = CodeMirror.optionHandlers;
9807
9808 var helpers = CodeMirror.helpers = {};
9809
9810 CodeMirror.prototype = {
9811 constructor: CodeMirror,
9812 focus: function(){window.focus(); this.display.input.focus();},
9813
9814 setOption: function(option, value) {
9815 var options = this.options, old = options[option];
9816 if (options[option] == value && option != "mode") { return }
9817 options[option] = value;
9818 if (optionHandlers.hasOwnProperty(option))
9819 { operation(this, optionHandlers[option])(this, value, old); }
9820 signal(this, "optionChange", this, option);
9821 },
9822
9823 getOption: function(option) {return this.options[option]},
9824 getDoc: function() {return this.doc},
9825
9826 addKeyMap: function(map, bottom) {
9827 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
9828 },
9829 removeKeyMap: function(map) {
9830 var maps = this.state.keyMaps;
9831 for (var i = 0; i < maps.length; ++i)
9832 { if (maps[i] == map || maps[i].name == map) {
9833 maps.splice(i, 1);
9834 return true
9835 } }
9836 },
9837
9838 addOverlay: methodOp(function(spec, options) {
9839 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9840 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9841 insertSorted(this.state.overlays,
9842 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9843 priority: (options && options.priority) || 0},
9844 function (overlay) { return overlay.priority; });
9845 this.state.modeGen++;
9846 regChange(this);
9847 }),
9848 removeOverlay: methodOp(function(spec) {
9849 var overlays = this.state.overlays;
9850 for (var i = 0; i < overlays.length; ++i) {
9851 var cur = overlays[i].modeSpec;
9852 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9853 overlays.splice(i, 1);
9854 this.state.modeGen++;
9855 regChange(this);
9856 return
9857 }
9858 }
9859 }),
9860
9861 indentLine: methodOp(function(n, dir, aggressive) {
9862 if (typeof dir != "string" && typeof dir != "number") {
9863 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9864 else { dir = dir ? "add" : "subtract"; }
9865 }
9866 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9867 }),
9868 indentSelection: methodOp(function(how) {
9869 var ranges = this.doc.sel.ranges, end = -1;
9870 for (var i = 0; i < ranges.length; i++) {
9871 var range = ranges[i];
9872 if (!range.empty()) {
9873 var from = range.from(), to = range.to();
9874 var start = Math.max(end, from.line);
9875 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9876 for (var j = start; j < end; ++j)
9877 { indentLine(this, j, how); }
9878 var newRanges = this.doc.sel.ranges;
9879 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9880 { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9881 } else if (range.head.line > end) {
9882 indentLine(this, range.head.line, how, true);
9883 end = range.head.line;
9884 if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }
9885 }
9886 }
9887 }),
9888
9889 // Fetch the parser token for a given character. Useful for hacks
9890 // that want to inspect the mode state (say, for completion).
9891 getTokenAt: function(pos, precise) {
9892 return takeToken(this, pos, precise)
9893 },
9894
9895 getLineTokens: function(line, precise) {
9896 return takeToken(this, Pos(line), precise, true)
9897 },
9898
9899 getTokenTypeAt: function(pos) {
9900 pos = clipPos(this.doc, pos);
9901 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9902 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9903 var type;
9904 if (ch == 0) { type = styles[2]; }
9905 else { for (;;) {
9906 var mid = (before + after) >> 1;
9907 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9908 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9909 else { type = styles[mid * 2 + 2]; break }
9910 } }
9911 var cut = type ? type.indexOf("overlay ") : -1;
9912 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9913 },
9914
9915 getModeAt: function(pos) {
9916 var mode = this.doc.mode;
9917 if (!mode.innerMode) { return mode }
9918 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9919 },
9920
9921 getHelper: function(pos, type) {
9922 return this.getHelpers(pos, type)[0]
9923 },
9924
9925 getHelpers: function(pos, type) {
9926 var found = [];
9927 if (!helpers.hasOwnProperty(type)) { return found }
9928 var help = helpers[type], mode = this.getModeAt(pos);
9929 if (typeof mode[type] == "string") {
9930 if (help[mode[type]]) { found.push(help[mode[type]]); }
9931 } else if (mode[type]) {
9932 for (var i = 0; i < mode[type].length; i++) {
9933 var val = help[mode[type][i]];
9934 if (val) { found.push(val); }
9935 }
9936 } else if (mode.helperType && help[mode.helperType]) {
9937 found.push(help[mode.helperType]);
9938 } else if (help[mode.name]) {
9939 found.push(help[mode.name]);
9940 }
9941 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9942 var cur = help._global[i$1];
9943 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
9944 { found.push(cur.val); }
9945 }
9946 return found
9947 },
9948
9949 getStateAfter: function(line, precise) {
9950 var doc = this.doc;
9951 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9952 return getContextBefore(this, line + 1, precise).state
9953 },
9954
9955 cursorCoords: function(start, mode) {
9956 var pos, range = this.doc.sel.primary();
9957 if (start == null) { pos = range.head; }
9958 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9959 else { pos = start ? range.from() : range.to(); }
9960 return cursorCoords(this, pos, mode || "page")
9961 },
9962
9963 charCoords: function(pos, mode) {
9964 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9965 },
9966
9967 coordsChar: function(coords, mode) {
9968 coords = fromCoordSystem(this, coords, mode || "page");
9969 return coordsChar(this, coords.left, coords.top)
9970 },
9971
9972 lineAtHeight: function(height, mode) {
9973 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9974 return lineAtHeight(this.doc, height + this.display.viewOffset)
9975 },
9976 heightAtLine: function(line, mode, includeWidgets) {
9977 var end = false, lineObj;
9978 if (typeof line == "number") {
9979 var last = this.doc.first + this.doc.size - 1;
9980 if (line < this.doc.first) { line = this.doc.first; }
9981 else if (line > last) { line = last; end = true; }
9982 lineObj = getLine(this.doc, line);
9983 } else {
9984 lineObj = line;
9985 }
9986 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9987 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9988 },
9989
9990 defaultTextHeight: function() { return textHeight(this.display) },
9991 defaultCharWidth: function() { return charWidth(this.display) },
9992
9993 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9994
9995 addWidget: function(pos, node, scroll, vert, horiz) {
9996 var display = this.display;
9997 pos = cursorCoords(this, clipPos(this.doc, pos));
9998 var top = pos.bottom, left = pos.left;
9999 node.style.position = "absolute";
10000 node.setAttribute("cm-ignore-events", "true");
10001 this.display.input.setUneditable(node);
10002 display.sizer.appendChild(node);
10003 if (vert == "over") {
10004 top = pos.top;
10005 } else if (vert == "above" || vert == "near") {
10006 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
10007 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
10008 // Default to positioning above (if specified and possible); otherwise default to positioning below
10009 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
10010 { top = pos.top - node.offsetHeight; }
10011 else if (pos.bottom + node.offsetHeight <= vspace)
10012 { top = pos.bottom; }
10013 if (left + node.offsetWidth > hspace)
10014 { left = hspace - node.offsetWidth; }
10015 }
10016 node.style.top = top + "px";
10017 node.style.left = node.style.right = "";
10018 if (horiz == "right") {
10019 left = display.sizer.clientWidth - node.offsetWidth;
10020 node.style.right = "0px";
10021 } else {
10022 if (horiz == "left") { left = 0; }
10023 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
10024 node.style.left = left + "px";
10025 }
10026 if (scroll)
10027 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
10028 },
10029
10030 triggerOnKeyDown: methodOp(onKeyDown),
10031 triggerOnKeyPress: methodOp(onKeyPress),
10032 triggerOnKeyUp: onKeyUp,
10033 triggerOnMouseDown: methodOp(onMouseDown),
10034
10035 execCommand: function(cmd) {
10036 if (commands.hasOwnProperty(cmd))
10037 { return commands[cmd].call(null, this) }
10038 },
10039
10040 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
10041
10042 findPosH: function(from, amount, unit, visually) {
10043 var dir = 1;
10044 if (amount < 0) { dir = -1; amount = -amount; }
10045 var cur = clipPos(this.doc, from);
10046 for (var i = 0; i < amount; ++i) {
10047 cur = findPosH(this.doc, cur, dir, unit, visually);
10048 if (cur.hitSide) { break }
10049 }
10050 return cur
10051 },
10052
10053 moveH: methodOp(function(dir, unit) {
10054 var this$1 = this;
10055
10056 this.extendSelectionsBy(function (range) {
10057 if (this$1.display.shift || this$1.doc.extend || range.empty())
10058 { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
10059 else
10060 { return dir < 0 ? range.from() : range.to() }
10061 }, sel_move);
10062 }),
10063
10064 deleteH: methodOp(function(dir, unit) {
10065 var sel = this.doc.sel, doc = this.doc;
10066 if (sel.somethingSelected())
10067 { doc.replaceSelection("", null, "+delete"); }
10068 else
10069 { deleteNearSelection(this, function (range) {
10070 var other = findPosH(doc, range.head, dir, unit, false);
10071 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
10072 }); }
10073 }),
10074
10075 findPosV: function(from, amount, unit, goalColumn) {
10076 var dir = 1, x = goalColumn;
10077 if (amount < 0) { dir = -1; amount = -amount; }
10078 var cur = clipPos(this.doc, from);
10079 for (var i = 0; i < amount; ++i) {
10080 var coords = cursorCoords(this, cur, "div");
10081 if (x == null) { x = coords.left; }
10082 else { coords.left = x; }
10083 cur = findPosV(this, coords, dir, unit);
10084 if (cur.hitSide) { break }
10085 }
10086 return cur
10087 },
10088
10089 moveV: methodOp(function(dir, unit) {
10090 var this$1 = this;
10091
10092 var doc = this.doc, goals = [];
10093 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
10094 doc.extendSelectionsBy(function (range) {
10095 if (collapse)
10096 { return dir < 0 ? range.from() : range.to() }
10097 var headPos = cursorCoords(this$1, range.head, "div");
10098 if (range.goalColumn != null) { headPos.left = range.goalColumn; }
10099 goals.push(headPos.left);
10100 var pos = findPosV(this$1, headPos, dir, unit);
10101 if (unit == "page" && range == doc.sel.primary())
10102 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
10103 return pos
10104 }, sel_move);
10105 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
10106 { doc.sel.ranges[i].goalColumn = goals[i]; } }
10107 }),
10108
10109 // Find the word at the given position (as returned by coordsChar).
10110 findWordAt: function(pos) {
10111 var doc = this.doc, line = getLine(doc, pos.line).text;
10112 var start = pos.ch, end = pos.ch;
10113 if (line) {
10114 var helper = this.getHelper(pos, "wordChars");
10115 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
10116 var startChar = line.charAt(start);
10117 var check = isWordChar(startChar, helper)
10118 ? function (ch) { return isWordChar(ch, helper); }
10119 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
10120 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
10121 while (start > 0 && check(line.charAt(start - 1))) { --start; }
10122 while (end < line.length && check(line.charAt(end))) { ++end; }
10123 }
10124 return new Range(Pos(pos.line, start), Pos(pos.line, end))
10125 },
10126
10127 toggleOverwrite: function(value) {
10128 if (value != null && value == this.state.overwrite) { return }
10129 if (this.state.overwrite = !this.state.overwrite)
10130 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10131 else
10132 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10133
10134 signal(this, "overwriteToggle", this, this.state.overwrite);
10135 },
10136 hasFocus: function() { return this.display.input.getField() == activeElt() },
10137 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
10138
10139 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
10140 getScrollInfo: function() {
10141 var scroller = this.display.scroller;
10142 return {left: scroller.scrollLeft, top: scroller.scrollTop,
10143 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
10144 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
10145 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
10146 },
10147
10148 scrollIntoView: methodOp(function(range, margin) {
10149 if (range == null) {
10150 range = {from: this.doc.sel.primary().head, to: null};
10151 if (margin == null) { margin = this.options.cursorScrollMargin; }
10152 } else if (typeof range == "number") {
10153 range = {from: Pos(range, 0), to: null};
10154 } else if (range.from == null) {
10155 range = {from: range, to: null};
10156 }
10157 if (!range.to) { range.to = range.from; }
10158 range.margin = margin || 0;
10159
10160 if (range.from.line != null) {
10161 scrollToRange(this, range);
10162 } else {
10163 scrollToCoordsRange(this, range.from, range.to, range.margin);
10164 }
10165 }),
10166
10167 setSize: methodOp(function(width, height) {
10168 var this$1 = this;
10169
10170 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
10171 if (width != null) { this.display.wrapper.style.width = interpret(width); }
10172 if (height != null) { this.display.wrapper.style.height = interpret(height); }
10173 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
10174 var lineNo = this.display.viewFrom;
10175 this.doc.iter(lineNo, this.display.viewTo, function (line) {
10176 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
10177 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
10178 ++lineNo;
10179 });
10180 this.curOp.forceUpdate = true;
10181 signal(this, "refresh", this);
10182 }),
10183
10184 operation: function(f){return runInOp(this, f)},
10185 startOperation: function(){return startOperation(this)},
10186 endOperation: function(){return endOperation(this)},
10187
10188 refresh: methodOp(function() {
10189 var oldHeight = this.display.cachedTextHeight;
10190 regChange(this);
10191 this.curOp.forceUpdate = true;
10192 clearCaches(this);
10193 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10194 updateGutterSpace(this.display);
10195 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
10196 { estimateLineHeights(this); }
10197 signal(this, "refresh", this);
10198 }),
10199
10200 swapDoc: methodOp(function(doc) {
10201 var old = this.doc;
10202 old.cm = null;
10203 // Cancel the current text selection if any (#5821)
10204 if (this.state.selectingText) { this.state.selectingText(); }
10205 attachDoc(this, doc);
10206 clearCaches(this);
10207 this.display.input.reset();
10208 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10209 this.curOp.forceScroll = true;
10210 signalLater(this, "swapDoc", this, old);
10211 return old
10212 }),
10213
10214 phrase: function(phraseText) {
10215 var phrases = this.options.phrases;
10216 return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
10217 },
10218
10219 getInputField: function(){return this.display.input.getField()},
10220 getWrapperElement: function(){return this.display.wrapper},
10221 getScrollerElement: function(){return this.display.scroller},
10222 getGutterElement: function(){return this.display.gutters}
10223 };
10224 eventMixin(CodeMirror);
10225
10226 CodeMirror.registerHelper = function(type, name, value) {
10227 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10228 helpers[type][name] = value;
10229 };
10230 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10231 CodeMirror.registerHelper(type, name, value);
10232 helpers[type]._global.push({pred: predicate, val: value});
10233 };
10234 }
10235
10236 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10237 // right), unit can be "char", "column" (like char, but doesn't
10238 // cross line boundaries), "word" (across next word), or "group" (to
10239 // the start of next group of word or non-word-non-whitespace
10240 // chars). The visually param controls whether, in right-to-left
10241 // text, direction 1 means to move towards the next index in the
10242 // string, or towards the character to the right of the current
10243 // position. The resulting position will have a hitSide=true
10244 // property if it reached the end of the document.
10245 function findPosH(doc, pos, dir, unit, visually) {
10246 var oldPos = pos;
10247 var origDir = dir;
10248 var lineObj = getLine(doc, pos.line);
10249 var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
10250 function findNextLine() {
10251 var l = pos.line + lineDir;
10252 if (l < doc.first || l >= doc.first + doc.size) { return false }
10253 pos = new Pos(l, pos.ch, pos.sticky);
10254 return lineObj = getLine(doc, l)
10255 }
10256 function moveOnce(boundToLine) {
10257 var next;
10258 if (visually) {
10259 next = moveVisually(doc.cm, lineObj, pos, dir);
10260 } else {
10261 next = moveLogically(lineObj, pos, dir);
10262 }
10263 if (next == null) {
10264 if (!boundToLine && findNextLine())
10265 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
10266 else
10267 { return false }
10268 } else {
10269 pos = next;
10270 }
10271 return true
10272 }
10273
10274 if (unit == "char") {
10275 moveOnce();
10276 } else if (unit == "column") {
10277 moveOnce(true);
10278 } else if (unit == "word" || unit == "group") {
10279 var sawType = null, group = unit == "group";
10280 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10281 for (var first = true;; first = false) {
10282 if (dir < 0 && !moveOnce(!first)) { break }
10283 var cur = lineObj.text.charAt(pos.ch) || "\n";
10284 var type = isWordChar(cur, helper) ? "w"
10285 : group && cur == "\n" ? "n"
10286 : !group || /\s/.test(cur) ? null
10287 : "p";
10288 if (group && !first && !type) { type = "s"; }
10289 if (sawType && sawType != type) {
10290 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10291 break
10292 }
10293
10294 if (type) { sawType = type; }
10295 if (dir > 0 && !moveOnce(!first)) { break }
10296 }
10297 }
10298 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10299 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10300 return result
10301 }
10302
10303 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10304 // "page" or "line". The resulting position will have a hitSide=true
10305 // property if it reached the end of the document.
10306 function findPosV(cm, pos, dir, unit) {
10307 var doc = cm.doc, x = pos.left, y;
10308 if (unit == "page") {
10309 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10310 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10311 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10312
10313 } else if (unit == "line") {
10314 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10315 }
10316 var target;
10317 for (;;) {
10318 target = coordsChar(cm, x, y);
10319 if (!target.outside) { break }
10320 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10321 y += dir * 5;
10322 }
10323 return target
10324 }
10325
10326 // CONTENTEDITABLE INPUT STYLE
10327
10328 var ContentEditableInput = function(cm) {
10329 this.cm = cm;
10330 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10331 this.polling = new Delayed();
10332 this.composing = null;
10333 this.gracePeriod = false;
10334 this.readDOMTimeout = null;
10335 };
10336
10337 ContentEditableInput.prototype.init = function (display) {
10338 var this$1 = this;
10339
10340 var input = this, cm = input.cm;
10341 var div = input.div = display.lineDiv;
10342 disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
10343
10344 on(div, "paste", function (e) {
10345 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10346 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10347 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10348 });
10349
10350 on(div, "compositionstart", function (e) {
10351 this$1.composing = {data: e.data, done: false};
10352 });
10353 on(div, "compositionupdate", function (e) {
10354 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10355 });
10356 on(div, "compositionend", function (e) {
10357 if (this$1.composing) {
10358 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10359 this$1.composing.done = true;
10360 }
10361 });
10362
10363 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10364
10365 on(div, "input", function () {
10366 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10367 });
10368
10369 function onCopyCut(e) {
10370 if (signalDOMEvent(cm, e)) { return }
10371 if (cm.somethingSelected()) {
10372 setLastCopied({lineWise: false, text: cm.getSelections()});
10373 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10374 } else if (!cm.options.lineWiseCopyCut) {
10375 return
10376 } else {
10377 var ranges = copyableRanges(cm);
10378 setLastCopied({lineWise: true, text: ranges.text});
10379 if (e.type == "cut") {
10380 cm.operation(function () {
10381 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10382 cm.replaceSelection("", null, "cut");
10383 });
10384 }
10385 }
10386 if (e.clipboardData) {
10387 e.clipboardData.clearData();
10388 var content = lastCopied.text.join("\n");
10389 // iOS exposes the clipboard API, but seems to discard content inserted into it
10390 e.clipboardData.setData("Text", content);
10391 if (e.clipboardData.getData("Text") == content) {
10392 e.preventDefault();
10393 return
10394 }
10395 }
10396 // Old-fashioned briefly-focus-a-textarea hack
10397 var kludge = hiddenTextarea(), te = kludge.firstChild;
10398 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10399 te.value = lastCopied.text.join("\n");
10400 var hadFocus = document.activeElement;
10401 selectInput(te);
10402 setTimeout(function () {
10403 cm.display.lineSpace.removeChild(kludge);
10404 hadFocus.focus();
10405 if (hadFocus == div) { input.showPrimarySelection(); }
10406 }, 50);
10407 }
10408 on(div, "copy", onCopyCut);
10409 on(div, "cut", onCopyCut);
10410 };
10411
10412 ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
10413 // Label for screenreaders, accessibility
10414 if(label) {
10415 this.div.setAttribute('aria-label', label);
10416 } else {
10417 this.div.removeAttribute('aria-label');
10418 }
10419 };
10420
10421 ContentEditableInput.prototype.prepareSelection = function () {
10422 var result = prepareSelection(this.cm, false);
10423 result.focus = document.activeElement == this.div;
10424 return result
10425 };
10426
10427 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10428 if (!info || !this.cm.display.view.length) { return }
10429 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10430 this.showMultipleSelections(info);
10431 };
10432
10433 ContentEditableInput.prototype.getSelection = function () {
10434 return this.cm.display.wrapper.ownerDocument.getSelection()
10435 };
10436
10437 ContentEditableInput.prototype.showPrimarySelection = function () {
10438 var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10439 var from = prim.from(), to = prim.to();
10440
10441 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10442 sel.removeAllRanges();
10443 return
10444 }
10445
10446 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10447 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10448 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10449 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10450 cmp(maxPos(curAnchor, curFocus), to) == 0)
10451 { return }
10452
10453 var view = cm.display.view;
10454 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10455 {node: view[0].measure.map[2], offset: 0};
10456 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10457 if (!end) {
10458 var measure = view[view.length - 1].measure;
10459 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10460 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
10461 }
10462
10463 if (!start || !end) {
10464 sel.removeAllRanges();
10465 return
10466 }
10467
10468 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10469 try { rng = range(start.node, start.offset, end.offset, end.node); }
10470 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10471 if (rng) {
10472 if (!gecko && cm.state.focused) {
10473 sel.collapse(start.node, start.offset);
10474 if (!rng.collapsed) {
10475 sel.removeAllRanges();
10476 sel.addRange(rng);
10477 }
10478 } else {
10479 sel.removeAllRanges();
10480 sel.addRange(rng);
10481 }
10482 if (old && sel.anchorNode == null) { sel.addRange(old); }
10483 else if (gecko) { this.startGracePeriod(); }
10484 }
10485 this.rememberSelection();
10486 };
10487
10488 ContentEditableInput.prototype.startGracePeriod = function () {
10489 var this$1 = this;
10490
10491 clearTimeout(this.gracePeriod);
10492 this.gracePeriod = setTimeout(function () {
10493 this$1.gracePeriod = false;
10494 if (this$1.selectionChanged())
10495 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10496 }, 20);
10497 };
10498
10499 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10500 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10501 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10502 };
10503
10504 ContentEditableInput.prototype.rememberSelection = function () {
10505 var sel = this.getSelection();
10506 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10507 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10508 };
10509
10510 ContentEditableInput.prototype.selectionInEditor = function () {
10511 var sel = this.getSelection();
10512 if (!sel.rangeCount) { return false }
10513 var node = sel.getRangeAt(0).commonAncestorContainer;
10514 return contains(this.div, node)
10515 };
10516
10517 ContentEditableInput.prototype.focus = function () {
10518 if (this.cm.options.readOnly != "nocursor") {
10519 if (!this.selectionInEditor() || document.activeElement != this.div)
10520 { this.showSelection(this.prepareSelection(), true); }
10521 this.div.focus();
10522 }
10523 };
10524 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10525 ContentEditableInput.prototype.getField = function () { return this.div };
10526
10527 ContentEditableInput.prototype.supportsTouch = function () { return true };
10528
10529 ContentEditableInput.prototype.receivedFocus = function () {
10530 var input = this;
10531 if (this.selectionInEditor())
10532 { this.pollSelection(); }
10533 else
10534 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10535
10536 function poll() {
10537 if (input.cm.state.focused) {
10538 input.pollSelection();
10539 input.polling.set(input.cm.options.pollInterval, poll);
10540 }
10541 }
10542 this.polling.set(this.cm.options.pollInterval, poll);
10543 };
10544
10545 ContentEditableInput.prototype.selectionChanged = function () {
10546 var sel = this.getSelection();
10547 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10548 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10549 };
10550
10551 ContentEditableInput.prototype.pollSelection = function () {
10552 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10553 var sel = this.getSelection(), cm = this.cm;
10554 // On Android Chrome (version 56, at least), backspacing into an
10555 // uneditable block element will put the cursor in that element,
10556 // and then, because it's not editable, hide the virtual keyboard.
10557 // Because Android doesn't allow us to actually detect backspace
10558 // presses in a sane way, this code checks for when that happens
10559 // and simulates a backspace press in this case.
10560 if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
10561 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10562 this.blur();
10563 this.focus();
10564 return
10565 }
10566 if (this.composing) { return }
10567 this.rememberSelection();
10568 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10569 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10570 if (anchor && head) { runInOp(cm, function () {
10571 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10572 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10573 }); }
10574 };
10575
10576 ContentEditableInput.prototype.pollContent = function () {
10577 if (this.readDOMTimeout != null) {
10578 clearTimeout(this.readDOMTimeout);
10579 this.readDOMTimeout = null;
10580 }
10581
10582 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10583 var from = sel.from(), to = sel.to();
10584 if (from.ch == 0 && from.line > cm.firstLine())
10585 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10586 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10587 { to = Pos(to.line + 1, 0); }
10588 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10589
10590 var fromIndex, fromLine, fromNode;
10591 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10592 fromLine = lineNo(display.view[0].line);
10593 fromNode = display.view[0].node;
10594 } else {
10595 fromLine = lineNo(display.view[fromIndex].line);
10596 fromNode = display.view[fromIndex - 1].node.nextSibling;
10597 }
10598 var toIndex = findViewIndex(cm, to.line);
10599 var toLine, toNode;
10600 if (toIndex == display.view.length - 1) {
10601 toLine = display.viewTo - 1;
10602 toNode = display.lineDiv.lastChild;
10603 } else {
10604 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10605 toNode = display.view[toIndex + 1].node.previousSibling;
10606 }
10607
10608 if (!fromNode) { return false }
10609 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10610 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10611 while (newText.length > 1 && oldText.length > 1) {
10612 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10613 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10614 else { break }
10615 }
10616
10617 var cutFront = 0, cutEnd = 0;
10618 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10619 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10620 { ++cutFront; }
10621 var newBot = lst(newText), oldBot = lst(oldText);
10622 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10623 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10624 while (cutEnd < maxCutEnd &&
10625 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10626 { ++cutEnd; }
10627 // Try to move start of change to start of selection if ambiguous
10628 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10629 while (cutFront && cutFront > from.ch &&
10630 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10631 cutFront--;
10632 cutEnd++;
10633 }
10634 }
10635
10636 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10637 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10638
10639 var chFrom = Pos(fromLine, cutFront);
10640 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10641 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10642 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10643 return true
10644 }
10645 };
10646
10647 ContentEditableInput.prototype.ensurePolled = function () {
10648 this.forceCompositionEnd();
10649 };
10650 ContentEditableInput.prototype.reset = function () {
10651 this.forceCompositionEnd();
10652 };
10653 ContentEditableInput.prototype.forceCompositionEnd = function () {
10654 if (!this.composing) { return }
10655 clearTimeout(this.readDOMTimeout);
10656 this.composing = null;
10657 this.updateFromDOM();
10658 this.div.blur();
10659 this.div.focus();
10660 };
10661 ContentEditableInput.prototype.readFromDOMSoon = function () {
10662 var this$1 = this;
10663
10664 if (this.readDOMTimeout != null) { return }
10665 this.readDOMTimeout = setTimeout(function () {
10666 this$1.readDOMTimeout = null;
10667 if (this$1.composing) {
10668 if (this$1.composing.done) { this$1.composing = null; }
10669 else { return }
10670 }
10671 this$1.updateFromDOM();
10672 }, 80);
10673 };
10674
10675 ContentEditableInput.prototype.updateFromDOM = function () {
10676 var this$1 = this;
10677
10678 if (this.cm.isReadOnly() || !this.pollContent())
10679 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10680 };
10681
10682 ContentEditableInput.prototype.setUneditable = function (node) {
10683 node.contentEditable = "false";
10684 };
10685
10686 ContentEditableInput.prototype.onKeyPress = function (e) {
10687 if (e.charCode == 0 || this.composing) { return }
10688 e.preventDefault();
10689 if (!this.cm.isReadOnly())
10690 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10691 };
10692
10693 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10694 this.div.contentEditable = String(val != "nocursor");
10695 };
10696
10697 ContentEditableInput.prototype.onContextMenu = function () {};
10698 ContentEditableInput.prototype.resetPosition = function () {};
10699
10700 ContentEditableInput.prototype.needsContentAttribute = true;
10701
10702 function posToDOM(cm, pos) {
10703 var view = findViewForLine(cm, pos.line);
10704 if (!view || view.hidden) { return null }
10705 var line = getLine(cm.doc, pos.line);
10706 var info = mapFromLineView(view, line, pos.line);
10707
10708 var order = getOrder(line, cm.doc.direction), side = "left";
10709 if (order) {
10710 var partPos = getBidiPartAt(order, pos.ch);
10711 side = partPos % 2 ? "right" : "left";
10712 }
10713 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10714 result.offset = result.collapse == "right" ? result.end : result.start;
10715 return result
10716 }
10717
10718 function isInGutter(node) {
10719 for (var scan = node; scan; scan = scan.parentNode)
10720 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10721 return false
10722 }
10723
10724 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10725
10726 function domTextBetween(cm, from, to, fromLine, toLine) {
10727 var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
10728 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10729 function close() {
10730 if (closing) {
10731 text += lineSep;
10732 if (extraLinebreak) { text += lineSep; }
10733 closing = extraLinebreak = false;
10734 }
10735 }
10736 function addText(str) {
10737 if (str) {
10738 close();
10739 text += str;
10740 }
10741 }
10742 function walk(node) {
10743 if (node.nodeType == 1) {
10744 var cmText = node.getAttribute("cm-text");
10745 if (cmText) {
10746 addText(cmText);
10747 return
10748 }
10749 var markerID = node.getAttribute("cm-marker"), range;
10750 if (markerID) {
10751 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10752 if (found.length && (range = found[0].find(0)))
10753 { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }
10754 return
10755 }
10756 if (node.getAttribute("contenteditable") == "false") { return }
10757 var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
10758 if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
10759
10760 if (isBlock) { close(); }
10761 for (var i = 0; i < node.childNodes.length; i++)
10762 { walk(node.childNodes[i]); }
10763
10764 if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
10765 if (isBlock) { closing = true; }
10766 } else if (node.nodeType == 3) {
10767 addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
10768 }
10769 }
10770 for (;;) {
10771 walk(from);
10772 if (from == to) { break }
10773 from = from.nextSibling;
10774 extraLinebreak = false;
10775 }
10776 return text
10777 }
10778
10779 function domToPos(cm, node, offset) {
10780 var lineNode;
10781 if (node == cm.display.lineDiv) {
10782 lineNode = cm.display.lineDiv.childNodes[offset];
10783 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10784 node = null; offset = 0;
10785 } else {
10786 for (lineNode = node;; lineNode = lineNode.parentNode) {
10787 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10788 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10789 }
10790 }
10791 for (var i = 0; i < cm.display.view.length; i++) {
10792 var lineView = cm.display.view[i];
10793 if (lineView.node == lineNode)
10794 { return locateNodeInLineView(lineView, node, offset) }
10795 }
10796 }
10797
10798 function locateNodeInLineView(lineView, node, offset) {
10799 var wrapper = lineView.text.firstChild, bad = false;
10800 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10801 if (node == wrapper) {
10802 bad = true;
10803 node = wrapper.childNodes[offset];
10804 offset = 0;
10805 if (!node) {
10806 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10807 return badPos(Pos(lineNo(line), line.text.length), bad)
10808 }
10809 }
10810
10811 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10812 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10813 textNode = node.firstChild;
10814 if (offset) { offset = textNode.nodeValue.length; }
10815 }
10816 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10817 var measure = lineView.measure, maps = measure.maps;
10818
10819 function find(textNode, topNode, offset) {
10820 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10821 var map = i < 0 ? measure.map : maps[i];
10822 for (var j = 0; j < map.length; j += 3) {
10823 var curNode = map[j + 2];
10824 if (curNode == textNode || curNode == topNode) {
10825 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10826 var ch = map[j] + offset;
10827 if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }
10828 return Pos(line, ch)
10829 }
10830 }
10831 }
10832 }
10833 var found = find(textNode, topNode, offset);
10834 if (found) { return badPos(found, bad) }
10835
10836 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10837 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10838 found = find(after, after.firstChild, 0);
10839 if (found)
10840 { return badPos(Pos(found.line, found.ch - dist), bad) }
10841 else
10842 { dist += after.textContent.length; }
10843 }
10844 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10845 found = find(before, before.firstChild, -1);
10846 if (found)
10847 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10848 else
10849 { dist$1 += before.textContent.length; }
10850 }
10851 }
10852
10853 // TEXTAREA INPUT STYLE
10854
10855 var TextareaInput = function(cm) {
10856 this.cm = cm;
10857 // See input.poll and input.reset
10858 this.prevInput = "";
10859
10860 // Flag that indicates whether we expect input to appear real soon
10861 // now (after some event like 'keypress' or 'input') and are
10862 // polling intensively.
10863 this.pollingFast = false;
10864 // Self-resetting timeout for the poller
10865 this.polling = new Delayed();
10866 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10867 this.hasSelection = false;
10868 this.composing = null;
10869 };
10870
10871 TextareaInput.prototype.init = function (display) {
10872 var this$1 = this;
10873
10874 var input = this, cm = this.cm;
10875 this.createField(display);
10876 var te = this.textarea;
10877
10878 display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
10879
10880 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10881 if (ios) { te.style.width = "0px"; }
10882
10883 on(te, "input", function () {
10884 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10885 input.poll();
10886 });
10887
10888 on(te, "paste", function (e) {
10889 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10890
10891 cm.state.pasteIncoming = +new Date;
10892 input.fastPoll();
10893 });
10894
10895 function prepareCopyCut(e) {
10896 if (signalDOMEvent(cm, e)) { return }
10897 if (cm.somethingSelected()) {
10898 setLastCopied({lineWise: false, text: cm.getSelections()});
10899 } else if (!cm.options.lineWiseCopyCut) {
10900 return
10901 } else {
10902 var ranges = copyableRanges(cm);
10903 setLastCopied({lineWise: true, text: ranges.text});
10904 if (e.type == "cut") {
10905 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10906 } else {
10907 input.prevInput = "";
10908 te.value = ranges.text.join("\n");
10909 selectInput(te);
10910 }
10911 }
10912 if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
10913 }
10914 on(te, "cut", prepareCopyCut);
10915 on(te, "copy", prepareCopyCut);
10916
10917 on(display.scroller, "paste", function (e) {
10918 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10919 if (!te.dispatchEvent) {
10920 cm.state.pasteIncoming = +new Date;
10921 input.focus();
10922 return
10923 }
10924
10925 // Pass the `paste` event to the textarea so it's handled by its event listener.
10926 var event = new Event("paste");
10927 event.clipboardData = e.clipboardData;
10928 te.dispatchEvent(event);
10929 });
10930
10931 // Prevent normal selection in the editor (we handle our own)
10932 on(display.lineSpace, "selectstart", function (e) {
10933 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10934 });
10935
10936 on(te, "compositionstart", function () {
10937 var start = cm.getCursor("from");
10938 if (input.composing) { input.composing.range.clear(); }
10939 input.composing = {
10940 start: start,
10941 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10942 };
10943 });
10944 on(te, "compositionend", function () {
10945 if (input.composing) {
10946 input.poll();
10947 input.composing.range.clear();
10948 input.composing = null;
10949 }
10950 });
10951 };
10952
10953 TextareaInput.prototype.createField = function (_display) {
10954 // Wraps and hides input textarea
10955 this.wrapper = hiddenTextarea();
10956 // The semihidden textarea that is focused when the editor is
10957 // focused, and receives input.
10958 this.textarea = this.wrapper.firstChild;
10959 };
10960
10961 TextareaInput.prototype.screenReaderLabelChanged = function (label) {
10962 // Label for screenreaders, accessibility
10963 if(label) {
10964 this.textarea.setAttribute('aria-label', label);
10965 } else {
10966 this.textarea.removeAttribute('aria-label');
10967 }
10968 };
10969
10970 TextareaInput.prototype.prepareSelection = function () {
10971 // Redraw the selection and/or cursor
10972 var cm = this.cm, display = cm.display, doc = cm.doc;
10973 var result = prepareSelection(cm);
10974
10975 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10976 if (cm.options.moveInputWithCursor) {
10977 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10978 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10979 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10980 headPos.top + lineOff.top - wrapOff.top));
10981 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10982 headPos.left + lineOff.left - wrapOff.left));
10983 }
10984
10985 return result
10986 };
10987
10988 TextareaInput.prototype.showSelection = function (drawn) {
10989 var cm = this.cm, display = cm.display;
10990 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10991 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10992 if (drawn.teTop != null) {
10993 this.wrapper.style.top = drawn.teTop + "px";
10994 this.wrapper.style.left = drawn.teLeft + "px";
10995 }
10996 };
10997
10998 // Reset the input to correspond to the selection (or to be empty,
10999 // when not typing and nothing is selected)
11000 TextareaInput.prototype.reset = function (typing) {
11001 if (this.contextMenuPending || this.composing) { return }
11002 var cm = this.cm;
11003 if (cm.somethingSelected()) {
11004 this.prevInput = "";
11005 var content = cm.getSelection();
11006 this.textarea.value = content;
11007 if (cm.state.focused) { selectInput(this.textarea); }
11008 if (ie && ie_version >= 9) { this.hasSelection = content; }
11009 } else if (!typing) {
11010 this.prevInput = this.textarea.value = "";
11011 if (ie && ie_version >= 9) { this.hasSelection = null; }
11012 }
11013 };
11014
11015 TextareaInput.prototype.getField = function () { return this.textarea };
11016
11017 TextareaInput.prototype.supportsTouch = function () { return false };
11018
11019 TextareaInput.prototype.focus = function () {
11020 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
11021 try { this.textarea.focus(); }
11022 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
11023 }
11024 };
11025
11026 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
11027
11028 TextareaInput.prototype.resetPosition = function () {
11029 this.wrapper.style.top = this.wrapper.style.left = 0;
11030 };
11031
11032 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
11033
11034 // Poll for input changes, using the normal rate of polling. This
11035 // runs as long as the editor is focused.
11036 TextareaInput.prototype.slowPoll = function () {
11037 var this$1 = this;
11038
11039 if (this.pollingFast) { return }
11040 this.polling.set(this.cm.options.pollInterval, function () {
11041 this$1.poll();
11042 if (this$1.cm.state.focused) { this$1.slowPoll(); }
11043 });
11044 };
11045
11046 // When an event has just come in that is likely to add or change
11047 // something in the input textarea, we poll faster, to ensure that
11048 // the change appears on the screen quickly.
11049 TextareaInput.prototype.fastPoll = function () {
11050 var missed = false, input = this;
11051 input.pollingFast = true;
11052 function p() {
11053 var changed = input.poll();
11054 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
11055 else {input.pollingFast = false; input.slowPoll();}
11056 }
11057 input.polling.set(20, p);
11058 };
11059
11060 // Read input from the textarea, and update the document to match.
11061 // When something is selected, it is present in the textarea, and
11062 // selected (unless it is huge, in which case a placeholder is
11063 // used). When nothing is selected, the cursor sits after previously
11064 // seen text (can be empty), which is stored in prevInput (we must
11065 // not reset the textarea when typing, because that breaks IME).
11066 TextareaInput.prototype.poll = function () {
11067 var this$1 = this;
11068
11069 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
11070 // Since this is called a *lot*, try to bail out as cheaply as
11071 // possible when it is clear that nothing happened. hasSelection
11072 // will be the case when there is a lot of text in the textarea,
11073 // in which case reading its value would be expensive.
11074 if (this.contextMenuPending || !cm.state.focused ||
11075 (hasSelection(input) && !prevInput && !this.composing) ||
11076 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
11077 { return false }
11078
11079 var text = input.value;
11080 // If nothing changed, bail.
11081 if (text == prevInput && !cm.somethingSelected()) { return false }
11082 // Work around nonsensical selection resetting in IE9/10, and
11083 // inexplicable appearance of private area unicode characters on
11084 // some key combos in Mac (#2689).
11085 if (ie && ie_version >= 9 && this.hasSelection === text ||
11086 mac && /[\uf700-\uf7ff]/.test(text)) {
11087 cm.display.input.reset();
11088 return false
11089 }
11090
11091 if (cm.doc.sel == cm.display.selForContextMenu) {
11092 var first = text.charCodeAt(0);
11093 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
11094 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
11095 }
11096 // Find the part of the input that is actually new
11097 var same = 0, l = Math.min(prevInput.length, text.length);
11098 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
11099
11100 runInOp(cm, function () {
11101 applyTextInput(cm, text.slice(same), prevInput.length - same,
11102 null, this$1.composing ? "*compose" : null);
11103
11104 // Don't leave long text in the textarea, since it makes further polling slow
11105 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
11106 else { this$1.prevInput = text; }
11107
11108 if (this$1.composing) {
11109 this$1.composing.range.clear();
11110 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
11111 {className: "CodeMirror-composing"});
11112 }
11113 });
11114 return true
11115 };
11116
11117 TextareaInput.prototype.ensurePolled = function () {
11118 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
11119 };
11120
11121 TextareaInput.prototype.onKeyPress = function () {
11122 if (ie && ie_version >= 9) { this.hasSelection = null; }
11123 this.fastPoll();
11124 };
11125
11126 TextareaInput.prototype.onContextMenu = function (e) {
11127 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
11128 if (input.contextMenuPending) { input.contextMenuPending(); }
11129 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
11130 if (!pos || presto) { return } // Opera is difficult.
11131
11132 // Reset the current text selection only if the click is done outside of the selection
11133 // and 'resetSelectionOnContextMenu' option is true.
11134 var reset = cm.options.resetSelectionOnContextMenu;
11135 if (reset && cm.doc.sel.contains(pos) == -1)
11136 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
11137
11138 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
11139 var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
11140 input.wrapper.style.cssText = "position: static";
11141 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);";
11142 var oldScrollY;
11143 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
11144 display.input.focus();
11145 if (webkit) { window.scrollTo(null, oldScrollY); }
11146 display.input.reset();
11147 // Adds "Select all" to context menu in FF
11148 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
11149 input.contextMenuPending = rehide;
11150 display.selForContextMenu = cm.doc.sel;
11151 clearTimeout(display.detectingSelectAll);
11152
11153 // Select-all will be greyed out if there's nothing to select, so
11154 // this adds a zero-width space so that we can later check whether
11155 // it got selected.
11156 function prepareSelectAllHack() {
11157 if (te.selectionStart != null) {
11158 var selected = cm.somethingSelected();
11159 var extval = "\u200b" + (selected ? te.value : "");
11160 te.value = "\u21da"; // Used to catch context-menu undo
11161 te.value = extval;
11162 input.prevInput = selected ? "" : "\u200b";
11163 te.selectionStart = 1; te.selectionEnd = extval.length;
11164 // Re-set this, in case some other handler touched the
11165 // selection in the meantime.
11166 display.selForContextMenu = cm.doc.sel;
11167 }
11168 }
11169 function rehide() {
11170 if (input.contextMenuPending != rehide) { return }
11171 input.contextMenuPending = false;
11172 input.wrapper.style.cssText = oldWrapperCSS;
11173 te.style.cssText = oldCSS;
11174 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
11175
11176 // Try to detect the user choosing select-all
11177 if (te.selectionStart != null) {
11178 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
11179 var i = 0, poll = function () {
11180 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
11181 te.selectionEnd > 0 && input.prevInput == "\u200b") {
11182 operation(cm, selectAll)(cm);
11183 } else if (i++ < 10) {
11184 display.detectingSelectAll = setTimeout(poll, 500);
11185 } else {
11186 display.selForContextMenu = null;
11187 display.input.reset();
11188 }
11189 };
11190 display.detectingSelectAll = setTimeout(poll, 200);
11191 }
11192 }
11193
11194 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
11195 if (captureRightClick) {
11196 e_stop(e);
11197 var mouseup = function () {
11198 off(window, "mouseup", mouseup);
11199 setTimeout(rehide, 20);
11200 };
11201 on(window, "mouseup", mouseup);
11202 } else {
11203 setTimeout(rehide, 50);
11204 }
11205 };
11206
11207 TextareaInput.prototype.readOnlyChanged = function (val) {
11208 if (!val) { this.reset(); }
11209 this.textarea.disabled = val == "nocursor";
11210 };
11211
11212 TextareaInput.prototype.setUneditable = function () {};
11213
11214 TextareaInput.prototype.needsContentAttribute = false;
11215
11216 function fromTextArea(textarea, options) {
11217 options = options ? copyObj(options) : {};
11218 options.value = textarea.value;
11219 if (!options.tabindex && textarea.tabIndex)
11220 { options.tabindex = textarea.tabIndex; }
11221 if (!options.placeholder && textarea.placeholder)
11222 { options.placeholder = textarea.placeholder; }
11223 // Set autofocus to true if this textarea is focused, or if it has
11224 // autofocus and no other element is focused.
11225 if (options.autofocus == null) {
11226 var hasFocus = activeElt();
11227 options.autofocus = hasFocus == textarea ||
11228 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
11229 }
11230
11231 function save() {textarea.value = cm.getValue();}
11232
11233 var realSubmit;
11234 if (textarea.form) {
11235 on(textarea.form, "submit", save);
11236 // Deplorable hack to make the submit method do the right thing.
11237 if (!options.leaveSubmitMethodAlone) {
11238 var form = textarea.form;
11239 realSubmit = form.submit;
11240 try {
11241 var wrappedSubmit = form.submit = function () {
11242 save();
11243 form.submit = realSubmit;
11244 form.submit();
11245 form.submit = wrappedSubmit;
11246 };
11247 } catch(e) {}
11248 }
11249 }
11250
11251 options.finishInit = function (cm) {
11252 cm.save = save;
11253 cm.getTextArea = function () { return textarea; };
11254 cm.toTextArea = function () {
11255 cm.toTextArea = isNaN; // Prevent this from being ran twice
11256 save();
11257 textarea.parentNode.removeChild(cm.getWrapperElement());
11258 textarea.style.display = "";
11259 if (textarea.form) {
11260 off(textarea.form, "submit", save);
11261 if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
11262 { textarea.form.submit = realSubmit; }
11263 }
11264 };
11265 };
11266
11267 textarea.style.display = "none";
11268 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11269 options);
11270 return cm
11271 }
11272
11273 function addLegacyProps(CodeMirror) {
11274 CodeMirror.off = off;
11275 CodeMirror.on = on;
11276 CodeMirror.wheelEventPixels = wheelEventPixels;
11277 CodeMirror.Doc = Doc;
11278 CodeMirror.splitLines = splitLinesAuto;
11279 CodeMirror.countColumn = countColumn;
11280 CodeMirror.findColumn = findColumn;
11281 CodeMirror.isWordChar = isWordCharBasic;
11282 CodeMirror.Pass = Pass;
11283 CodeMirror.signal = signal;
11284 CodeMirror.Line = Line;
11285 CodeMirror.changeEnd = changeEnd;
11286 CodeMirror.scrollbarModel = scrollbarModel;
11287 CodeMirror.Pos = Pos;
11288 CodeMirror.cmpPos = cmp;
11289 CodeMirror.modes = modes;
11290 CodeMirror.mimeModes = mimeModes;
11291 CodeMirror.resolveMode = resolveMode;
11292 CodeMirror.getMode = getMode;
11293 CodeMirror.modeExtensions = modeExtensions;
11294 CodeMirror.extendMode = extendMode;
11295 CodeMirror.copyState = copyState;
11296 CodeMirror.startState = startState;
11297 CodeMirror.innerMode = innerMode;
11298 CodeMirror.commands = commands;
11299 CodeMirror.keyMap = keyMap;
11300 CodeMirror.keyName = keyName;
11301 CodeMirror.isModifierKey = isModifierKey;
11302 CodeMirror.lookupKey = lookupKey;
11303 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11304 CodeMirror.StringStream = StringStream;
11305 CodeMirror.SharedTextMarker = SharedTextMarker;
11306 CodeMirror.TextMarker = TextMarker;
11307 CodeMirror.LineWidget = LineWidget;
11308 CodeMirror.e_preventDefault = e_preventDefault;
11309 CodeMirror.e_stopPropagation = e_stopPropagation;
11310 CodeMirror.e_stop = e_stop;
11311 CodeMirror.addClass = addClass;
11312 CodeMirror.contains = contains;
11313 CodeMirror.rmClass = rmClass;
11314 CodeMirror.keyNames = keyNames;
11315 }
11316
11317 // EDITOR CONSTRUCTOR
11318
11319 defineOptions(CodeMirror);
11320
11321 addEditorMethods(CodeMirror);
11322
11323 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11324 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11325 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11326 { CodeMirror.prototype[prop] = (function(method) {
11327 return function() {return method.apply(this.doc, arguments)}
11328 })(Doc.prototype[prop]); } }
11329
11330 eventMixin(Doc);
11331 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11332
11333 // Extra arguments are stored as the mode's dependencies, which is
11334 // used by (legacy) mechanisms like loadmode.js to automatically
11335 // load a mode. (Preferred mechanism is the require/define calls.)
11336 CodeMirror.defineMode = function(name/*, mode, …*/) {
11337 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
11338 defineMode.apply(this, arguments);
11339 };
11340
11341 CodeMirror.defineMIME = defineMIME;
11342
11343 // Minimal default mode.
11344 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11345 CodeMirror.defineMIME("text/plain", "null");
11346
11347 // EXTENSIONS
11348
11349 CodeMirror.defineExtension = function (name, func) {
11350 CodeMirror.prototype[name] = func;
11351 };
11352 CodeMirror.defineDocExtension = function (name, func) {
11353 Doc.prototype[name] = func;
11354 };
11355
11356 CodeMirror.fromTextArea = fromTextArea;
11357
11358 addLegacyProps(CodeMirror);
11359
11360 CodeMirror.version = "5.53.2";
11361
11362 return CodeMirror;
11363
11364 })));
11365
11366 },{}],15:[function(require,module,exports){
11367 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11368 // Distributed under an MIT license: https://codemirror.net/LICENSE
11369
11370 (function(mod) {
11371 if (typeof exports == "object" && typeof module == "object") // CommonJS
11372 mod(require("../../lib/codemirror"));
11373 else if (typeof define == "function" && define.amd) // AMD
11374 define(["../../lib/codemirror"], mod);
11375 else // Plain browser env
11376 mod(CodeMirror);
11377 })(function(CodeMirror) {
11378 "use strict";
11379
11380 CodeMirror.defineMode("css", function(config, parserConfig) {
11381 var inline = parserConfig.inline
11382 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11383
11384 var indentUnit = config.indentUnit,
11385 tokenHooks = parserConfig.tokenHooks,
11386 documentTypes = parserConfig.documentTypes || {},
11387 mediaTypes = parserConfig.mediaTypes || {},
11388 mediaFeatures = parserConfig.mediaFeatures || {},
11389 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11390 propertyKeywords = parserConfig.propertyKeywords || {},
11391 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11392 fontProperties = parserConfig.fontProperties || {},
11393 counterDescriptors = parserConfig.counterDescriptors || {},
11394 colorKeywords = parserConfig.colorKeywords || {},
11395 valueKeywords = parserConfig.valueKeywords || {},
11396 allowNested = parserConfig.allowNested,
11397 lineComment = parserConfig.lineComment,
11398 supportsAtComponent = parserConfig.supportsAtComponent === true;
11399
11400 var type, override;
11401 function ret(style, tp) { type = tp; return style; }
11402
11403 // Tokenizers
11404
11405 function tokenBase(stream, state) {
11406 var ch = stream.next();
11407 if (tokenHooks[ch]) {
11408 var result = tokenHooks[ch](stream, state);
11409 if (result !== false) return result;
11410 }
11411 if (ch == "@") {
11412 stream.eatWhile(/[\w\\\-]/);
11413 return ret("def", stream.current());
11414 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11415 return ret(null, "compare");
11416 } else if (ch == "\"" || ch == "'") {
11417 state.tokenize = tokenString(ch);
11418 return state.tokenize(stream, state);
11419 } else if (ch == "#") {
11420 stream.eatWhile(/[\w\\\-]/);
11421 return ret("atom", "hash");
11422 } else if (ch == "!") {
11423 stream.match(/^\s*\w*/);
11424 return ret("keyword", "important");
11425 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11426 stream.eatWhile(/[\w.%]/);
11427 return ret("number", "unit");
11428 } else if (ch === "-") {
11429 if (/[\d.]/.test(stream.peek())) {
11430 stream.eatWhile(/[\w.%]/);
11431 return ret("number", "unit");
11432 } else if (stream.match(/^-[\w\\\-]*/)) {
11433 stream.eatWhile(/[\w\\\-]/);
11434 if (stream.match(/^\s*:/, false))
11435 return ret("variable-2", "variable-definition");
11436 return ret("variable-2", "variable");
11437 } else if (stream.match(/^\w+-/)) {
11438 return ret("meta", "meta");
11439 }
11440 } else if (/[,+>*\/]/.test(ch)) {
11441 return ret(null, "select-op");
11442 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11443 return ret("qualifier", "qualifier");
11444 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11445 return ret(null, ch);
11446 } else if (stream.match(/[\w-.]+(?=\()/)) {
11447 if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
11448 state.tokenize = tokenParenthesized;
11449 }
11450 return ret("variable callee", "variable");
11451 } else if (/[\w\\\-]/.test(ch)) {
11452 stream.eatWhile(/[\w\\\-]/);
11453 return ret("property", "word");
11454 } else {
11455 return ret(null, null);
11456 }
11457 }
11458
11459 function tokenString(quote) {
11460 return function(stream, state) {
11461 var escaped = false, ch;
11462 while ((ch = stream.next()) != null) {
11463 if (ch == quote && !escaped) {
11464 if (quote == ")") stream.backUp(1);
11465 break;
11466 }
11467 escaped = !escaped && ch == "\\";
11468 }
11469 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11470 return ret("string", "string");
11471 };
11472 }
11473
11474 function tokenParenthesized(stream, state) {
11475 stream.next(); // Must be '('
11476 if (!stream.match(/\s*[\"\')]/, false))
11477 state.tokenize = tokenString(")");
11478 else
11479 state.tokenize = null;
11480 return ret(null, "(");
11481 }
11482
11483 // Context management
11484
11485 function Context(type, indent, prev) {
11486 this.type = type;
11487 this.indent = indent;
11488 this.prev = prev;
11489 }
11490
11491 function pushContext(state, stream, type, indent) {
11492 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11493 return type;
11494 }
11495
11496 function popContext(state) {
11497 if (state.context.prev)
11498 state.context = state.context.prev;
11499 return state.context.type;
11500 }
11501
11502 function pass(type, stream, state) {
11503 return states[state.context.type](type, stream, state);
11504 }
11505 function popAndPass(type, stream, state, n) {
11506 for (var i = n || 1; i > 0; i--)
11507 state.context = state.context.prev;
11508 return pass(type, stream, state);
11509 }
11510
11511 // Parser
11512
11513 function wordAsValue(stream) {
11514 var word = stream.current().toLowerCase();
11515 if (valueKeywords.hasOwnProperty(word))
11516 override = "atom";
11517 else if (colorKeywords.hasOwnProperty(word))
11518 override = "keyword";
11519 else
11520 override = "variable";
11521 }
11522
11523 var states = {};
11524
11525 states.top = function(type, stream, state) {
11526 if (type == "{") {
11527 return pushContext(state, stream, "block");
11528 } else if (type == "}" && state.context.prev) {
11529 return popContext(state);
11530 } else if (supportsAtComponent && /@component/i.test(type)) {
11531 return pushContext(state, stream, "atComponentBlock");
11532 } else if (/^@(-moz-)?document$/i.test(type)) {
11533 return pushContext(state, stream, "documentTypes");
11534 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11535 return pushContext(state, stream, "atBlock");
11536 } else if (/^@(font-face|counter-style)/i.test(type)) {
11537 state.stateArg = type;
11538 return "restricted_atBlock_before";
11539 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11540 return "keyframes";
11541 } else if (type && type.charAt(0) == "@") {
11542 return pushContext(state, stream, "at");
11543 } else if (type == "hash") {
11544 override = "builtin";
11545 } else if (type == "word") {
11546 override = "tag";
11547 } else if (type == "variable-definition") {
11548 return "maybeprop";
11549 } else if (type == "interpolation") {
11550 return pushContext(state, stream, "interpolation");
11551 } else if (type == ":") {
11552 return "pseudo";
11553 } else if (allowNested && type == "(") {
11554 return pushContext(state, stream, "parens");
11555 }
11556 return state.context.type;
11557 };
11558
11559 states.block = function(type, stream, state) {
11560 if (type == "word") {
11561 var word = stream.current().toLowerCase();
11562 if (propertyKeywords.hasOwnProperty(word)) {
11563 override = "property";
11564 return "maybeprop";
11565 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11566 override = "string-2";
11567 return "maybeprop";
11568 } else if (allowNested) {
11569 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11570 return "block";
11571 } else {
11572 override += " error";
11573 return "maybeprop";
11574 }
11575 } else if (type == "meta") {
11576 return "block";
11577 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11578 override = "error";
11579 return "block";
11580 } else {
11581 return states.top(type, stream, state);
11582 }
11583 };
11584
11585 states.maybeprop = function(type, stream, state) {
11586 if (type == ":") return pushContext(state, stream, "prop");
11587 return pass(type, stream, state);
11588 };
11589
11590 states.prop = function(type, stream, state) {
11591 if (type == ";") return popContext(state);
11592 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11593 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11594 if (type == "(") return pushContext(state, stream, "parens");
11595
11596 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11597 override += " error";
11598 } else if (type == "word") {
11599 wordAsValue(stream);
11600 } else if (type == "interpolation") {
11601 return pushContext(state, stream, "interpolation");
11602 }
11603 return "prop";
11604 };
11605
11606 states.propBlock = function(type, _stream, state) {
11607 if (type == "}") return popContext(state);
11608 if (type == "word") { override = "property"; return "maybeprop"; }
11609 return state.context.type;
11610 };
11611
11612 states.parens = function(type, stream, state) {
11613 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11614 if (type == ")") return popContext(state);
11615 if (type == "(") return pushContext(state, stream, "parens");
11616 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11617 if (type == "word") wordAsValue(stream);
11618 return "parens";
11619 };
11620
11621 states.pseudo = function(type, stream, state) {
11622 if (type == "meta") return "pseudo";
11623
11624 if (type == "word") {
11625 override = "variable-3";
11626 return state.context.type;
11627 }
11628 return pass(type, stream, state);
11629 };
11630
11631 states.documentTypes = function(type, stream, state) {
11632 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11633 override = "tag";
11634 return state.context.type;
11635 } else {
11636 return states.atBlock(type, stream, state);
11637 }
11638 };
11639
11640 states.atBlock = function(type, stream, state) {
11641 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11642 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11643 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11644
11645 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11646
11647 if (type == "word") {
11648 var word = stream.current().toLowerCase();
11649 if (word == "only" || word == "not" || word == "and" || word == "or")
11650 override = "keyword";
11651 else if (mediaTypes.hasOwnProperty(word))
11652 override = "attribute";
11653 else if (mediaFeatures.hasOwnProperty(word))
11654 override = "property";
11655 else if (mediaValueKeywords.hasOwnProperty(word))
11656 override = "keyword";
11657 else if (propertyKeywords.hasOwnProperty(word))
11658 override = "property";
11659 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11660 override = "string-2";
11661 else if (valueKeywords.hasOwnProperty(word))
11662 override = "atom";
11663 else if (colorKeywords.hasOwnProperty(word))
11664 override = "keyword";
11665 else
11666 override = "error";
11667 }
11668 return state.context.type;
11669 };
11670
11671 states.atComponentBlock = function(type, stream, state) {
11672 if (type == "}")
11673 return popAndPass(type, stream, state);
11674 if (type == "{")
11675 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11676 if (type == "word")
11677 override = "error";
11678 return state.context.type;
11679 };
11680
11681 states.atBlock_parens = function(type, stream, state) {
11682 if (type == ")") return popContext(state);
11683 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11684 return states.atBlock(type, stream, state);
11685 };
11686
11687 states.restricted_atBlock_before = function(type, stream, state) {
11688 if (type == "{")
11689 return pushContext(state, stream, "restricted_atBlock");
11690 if (type == "word" && state.stateArg == "@counter-style") {
11691 override = "variable";
11692 return "restricted_atBlock_before";
11693 }
11694 return pass(type, stream, state);
11695 };
11696
11697 states.restricted_atBlock = function(type, stream, state) {
11698 if (type == "}") {
11699 state.stateArg = null;
11700 return popContext(state);
11701 }
11702 if (type == "word") {
11703 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11704 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11705 override = "error";
11706 else
11707 override = "property";
11708 return "maybeprop";
11709 }
11710 return "restricted_atBlock";
11711 };
11712
11713 states.keyframes = function(type, stream, state) {
11714 if (type == "word") { override = "variable"; return "keyframes"; }
11715 if (type == "{") return pushContext(state, stream, "top");
11716 return pass(type, stream, state);
11717 };
11718
11719 states.at = function(type, stream, state) {
11720 if (type == ";") return popContext(state);
11721 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11722 if (type == "word") override = "tag";
11723 else if (type == "hash") override = "builtin";
11724 return "at";
11725 };
11726
11727 states.interpolation = function(type, stream, state) {
11728 if (type == "}") return popContext(state);
11729 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11730 if (type == "word") override = "variable";
11731 else if (type != "variable" && type != "(" && type != ")") override = "error";
11732 return "interpolation";
11733 };
11734
11735 return {
11736 startState: function(base) {
11737 return {tokenize: null,
11738 state: inline ? "block" : "top",
11739 stateArg: null,
11740 context: new Context(inline ? "block" : "top", base || 0, null)};
11741 },
11742
11743 token: function(stream, state) {
11744 if (!state.tokenize && stream.eatSpace()) return null;
11745 var style = (state.tokenize || tokenBase)(stream, state);
11746 if (style && typeof style == "object") {
11747 type = style[1];
11748 style = style[0];
11749 }
11750 override = style;
11751 if (type != "comment")
11752 state.state = states[state.state](type, stream, state);
11753 return override;
11754 },
11755
11756 indent: function(state, textAfter) {
11757 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11758 var indent = cx.indent;
11759 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11760 if (cx.prev) {
11761 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11762 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11763 // Resume indentation from parent context.
11764 cx = cx.prev;
11765 indent = cx.indent;
11766 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11767 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11768 // Dedent relative to current context.
11769 indent = Math.max(0, cx.indent - indentUnit);
11770 }
11771 }
11772 return indent;
11773 },
11774
11775 electricChars: "}",
11776 blockCommentStart: "/*",
11777 blockCommentEnd: "*/",
11778 blockCommentContinue: " * ",
11779 lineComment: lineComment,
11780 fold: "brace"
11781 };
11782 });
11783
11784 function keySet(array) {
11785 var keys = {};
11786 for (var i = 0; i < array.length; ++i) {
11787 keys[array[i].toLowerCase()] = true;
11788 }
11789 return keys;
11790 }
11791
11792 var documentTypes_ = [
11793 "domain", "regexp", "url", "url-prefix"
11794 ], documentTypes = keySet(documentTypes_);
11795
11796 var mediaTypes_ = [
11797 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11798 "tty", "tv", "embossed"
11799 ], mediaTypes = keySet(mediaTypes_);
11800
11801 var mediaFeatures_ = [
11802 "width", "min-width", "max-width", "height", "min-height", "max-height",
11803 "device-width", "min-device-width", "max-device-width", "device-height",
11804 "min-device-height", "max-device-height", "aspect-ratio",
11805 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11806 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11807 "max-color", "color-index", "min-color-index", "max-color-index",
11808 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11809 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11810 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11811 "pointer", "any-pointer", "hover", "any-hover"
11812 ], mediaFeatures = keySet(mediaFeatures_);
11813
11814 var mediaValueKeywords_ = [
11815 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11816 "interlace", "progressive"
11817 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11818
11819 var propertyKeywords_ = [
11820 "align-content", "align-items", "align-self", "alignment-adjust",
11821 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11822 "animation-direction", "animation-duration", "animation-fill-mode",
11823 "animation-iteration-count", "animation-name", "animation-play-state",
11824 "animation-timing-function", "appearance", "azimuth", "backdrop-filter",
11825 "backface-visibility", "background", "background-attachment",
11826 "background-blend-mode", "background-clip", "background-color",
11827 "background-image", "background-origin", "background-position",
11828 "background-position-x", "background-position-y", "background-repeat",
11829 "background-size", "baseline-shift", "binding", "bleed", "block-size",
11830 "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target",
11831 "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
11832 "border-bottom-right-radius", "border-bottom-style", "border-bottom-width",
11833 "border-collapse", "border-color", "border-image", "border-image-outset",
11834 "border-image-repeat", "border-image-slice", "border-image-source",
11835 "border-image-width", "border-left", "border-left-color", "border-left-style",
11836 "border-left-width", "border-radius", "border-right", "border-right-color",
11837 "border-right-style", "border-right-width", "border-spacing", "border-style",
11838 "border-top", "border-top-color", "border-top-left-radius",
11839 "border-top-right-radius", "border-top-style", "border-top-width",
11840 "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing",
11841 "break-after", "break-before", "break-inside", "caption-side", "caret-color",
11842 "clear", "clip", "color", "color-profile", "column-count", "column-fill",
11843 "column-gap", "column-rule", "column-rule-color", "column-rule-style",
11844 "column-rule-width", "column-span", "column-width", "columns", "contain",
11845 "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after",
11846 "cue-before", "cursor", "direction", "display", "dominant-baseline",
11847 "drop-initial-after-adjust", "drop-initial-after-align",
11848 "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
11849 "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position",
11850 "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow",
11851 "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into",
11852 "font", "font-family", "font-feature-settings", "font-kerning",
11853 "font-language-override", "font-optical-sizing", "font-size",
11854 "font-size-adjust", "font-stretch", "font-style", "font-synthesis",
11855 "font-variant", "font-variant-alternates", "font-variant-caps",
11856 "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric",
11857 "font-variant-position", "font-variation-settings", "font-weight", "gap",
11858 "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
11859 "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start",
11860 "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start",
11861 "grid-template", "grid-template-areas", "grid-template-columns",
11862 "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon",
11863 "image-orientation", "image-rendering", "image-resolution", "inline-box-align",
11864 "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline",
11865 "inset-inline-end", "inset-inline-start", "isolation", "justify-content",
11866 "justify-items", "justify-self", "left", "letter-spacing", "line-break",
11867 "line-height", "line-height-step", "line-stacking", "line-stacking-ruby",
11868 "line-stacking-shift", "line-stacking-strategy", "list-style",
11869 "list-style-image", "list-style-position", "list-style-type", "margin",
11870 "margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
11871 "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
11872 "marquee-style", "max-block-size", "max-height", "max-inline-size",
11873 "max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
11874 "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
11875 "nav-up", "object-fit", "object-position", "offset", "offset-anchor",
11876 "offset-distance", "offset-path", "offset-position", "offset-rotate",
11877 "opacity", "order", "orphans", "outline", "outline-color", "outline-offset",
11878 "outline-style", "outline-width", "overflow", "overflow-style",
11879 "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom",
11880 "padding-left", "padding-right", "padding-top", "page", "page-break-after",
11881 "page-break-before", "page-break-inside", "page-policy", "pause",
11882 "pause-after", "pause-before", "perspective", "perspective-origin", "pitch",
11883 "pitch-range", "place-content", "place-items", "place-self", "play-during",
11884 "position", "presentation-level", "punctuation-trim", "quotes",
11885 "region-break-after", "region-break-before", "region-break-inside",
11886 "region-fragment", "rendering-intent", "resize", "rest", "rest-after",
11887 "rest-before", "richness", "right", "rotate", "rotation", "rotation-point",
11888 "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span",
11889 "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block",
11890 "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom",
11891 "scroll-margin-inline", "scroll-margin-inline-end",
11892 "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right",
11893 "scroll-margin-top", "scroll-padding", "scroll-padding-block",
11894 "scroll-padding-block-end", "scroll-padding-block-start",
11895 "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end",
11896 "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right",
11897 "scroll-padding-top", "scroll-snap-align", "scroll-snap-type",
11898 "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside",
11899 "size", "speak", "speak-as", "speak-header", "speak-numeral",
11900 "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size",
11901 "table-layout", "target", "target-name", "target-new", "target-position",
11902 "text-align", "text-align-last", "text-combine-upright", "text-decoration",
11903 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11904 "text-decoration-skip-ink", "text-decoration-style", "text-emphasis",
11905 "text-emphasis-color", "text-emphasis-position", "text-emphasis-style",
11906 "text-height", "text-indent", "text-justify", "text-orientation",
11907 "text-outline", "text-overflow", "text-rendering", "text-shadow",
11908 "text-size-adjust", "text-space-collapse", "text-transform",
11909 "text-underline-position", "text-wrap", "top", "transform", "transform-origin",
11910 "transform-style", "transition", "transition-delay", "transition-duration",
11911 "transition-property", "transition-timing-function", "translate",
11912 "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance",
11913 "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate",
11914 "voice-stress", "voice-volume", "volume", "white-space", "widows", "width",
11915 "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index",
11916 // SVG-specific
11917 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11918 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11919 "color-interpolation", "color-interpolation-filters",
11920 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11921 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11922 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11923 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11924 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11925 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11926 ], propertyKeywords = keySet(propertyKeywords_);
11927
11928 var nonStandardPropertyKeywords_ = [
11929 "border-block", "border-block-color", "border-block-end",
11930 "border-block-end-color", "border-block-end-style", "border-block-end-width",
11931 "border-block-start", "border-block-start-color", "border-block-start-style",
11932 "border-block-start-width", "border-block-style", "border-block-width",
11933 "border-inline", "border-inline-color", "border-inline-end",
11934 "border-inline-end-color", "border-inline-end-style",
11935 "border-inline-end-width", "border-inline-start", "border-inline-start-color",
11936 "border-inline-start-style", "border-inline-start-width",
11937 "border-inline-style", "border-inline-width", "margin-block",
11938 "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end",
11939 "margin-inline-start", "padding-block", "padding-block-end",
11940 "padding-block-start", "padding-inline", "padding-inline-end",
11941 "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color",
11942 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11943 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11944 "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration",
11945 "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom"
11946 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11947
11948 var fontProperties_ = [
11949 "font-display", "font-family", "src", "unicode-range", "font-variant",
11950 "font-feature-settings", "font-stretch", "font-weight", "font-style"
11951 ], fontProperties = keySet(fontProperties_);
11952
11953 var counterDescriptors_ = [
11954 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11955 "speak-as", "suffix", "symbols", "system"
11956 ], counterDescriptors = keySet(counterDescriptors_);
11957
11958 var colorKeywords_ = [
11959 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11960 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11961 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11962 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11963 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11964 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11965 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11966 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11967 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11968 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11969 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11970 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11971 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11972 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11973 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11974 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11975 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11976 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11977 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11978 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11979 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11980 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11981 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11982 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11983 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11984 "whitesmoke", "yellow", "yellowgreen"
11985 ], colorKeywords = keySet(colorKeywords_);
11986
11987 var valueKeywords_ = [
11988 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11989 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11990 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11991 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11992 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11993 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11994 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11995 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11996 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11997 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11998 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11999 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
12000 "compact", "condensed", "contain", "content", "contents",
12001 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
12002 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
12003 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
12004 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
12005 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
12006 "dot-dash", "dot-dot-dash",
12007 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
12008 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
12009 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
12010 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
12011 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
12012 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
12013 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
12014 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
12015 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
12016 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
12017 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
12018 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
12019 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
12020 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
12021 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
12022 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
12023 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
12024 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
12025 "katakana", "katakana-iroha", "keep-all", "khmer",
12026 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
12027 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
12028 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
12029 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
12030 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
12031 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
12032 "media-controls-background", "media-current-time-display",
12033 "media-fullscreen-button", "media-mute-button", "media-play-button",
12034 "media-return-to-realtime-button", "media-rewind-button",
12035 "media-seek-back-button", "media-seek-forward-button", "media-slider",
12036 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
12037 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
12038 "menu", "menulist", "menulist-button", "menulist-text",
12039 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
12040 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
12041 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
12042 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
12043 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
12044 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
12045 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
12046 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
12047 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
12048 "progress", "push-button", "radial-gradient", "radio", "read-only",
12049 "read-write", "read-write-plaintext-only", "rectangle", "region",
12050 "relative", "repeat", "repeating-linear-gradient",
12051 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
12052 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
12053 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
12054 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
12055 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
12056 "searchfield-cancel-button", "searchfield-decoration",
12057 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
12058 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
12059 "simp-chinese-formal", "simp-chinese-informal", "single",
12060 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
12061 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
12062 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
12063 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
12064 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
12065 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
12066 "table-caption", "table-cell", "table-column", "table-column-group",
12067 "table-footer-group", "table-header-group", "table-row", "table-row-group",
12068 "tamil",
12069 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
12070 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
12071 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
12072 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
12073 "trad-chinese-formal", "trad-chinese-informal", "transform",
12074 "translate", "translate3d", "translateX", "translateY", "translateZ",
12075 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
12076 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
12077 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
12078 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
12079 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
12080 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
12081 "xx-large", "xx-small"
12082 ], valueKeywords = keySet(valueKeywords_);
12083
12084 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
12085 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
12086 .concat(valueKeywords_);
12087 CodeMirror.registerHelper("hintWords", "css", allWords);
12088
12089 function tokenCComment(stream, state) {
12090 var maybeEnd = false, ch;
12091 while ((ch = stream.next()) != null) {
12092 if (maybeEnd && ch == "/") {
12093 state.tokenize = null;
12094 break;
12095 }
12096 maybeEnd = (ch == "*");
12097 }
12098 return ["comment", "comment"];
12099 }
12100
12101 CodeMirror.defineMIME("text/css", {
12102 documentTypes: documentTypes,
12103 mediaTypes: mediaTypes,
12104 mediaFeatures: mediaFeatures,
12105 mediaValueKeywords: mediaValueKeywords,
12106 propertyKeywords: propertyKeywords,
12107 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12108 fontProperties: fontProperties,
12109 counterDescriptors: counterDescriptors,
12110 colorKeywords: colorKeywords,
12111 valueKeywords: valueKeywords,
12112 tokenHooks: {
12113 "/": function(stream, state) {
12114 if (!stream.eat("*")) return false;
12115 state.tokenize = tokenCComment;
12116 return tokenCComment(stream, state);
12117 }
12118 },
12119 name: "css"
12120 });
12121
12122 CodeMirror.defineMIME("text/x-scss", {
12123 mediaTypes: mediaTypes,
12124 mediaFeatures: mediaFeatures,
12125 mediaValueKeywords: mediaValueKeywords,
12126 propertyKeywords: propertyKeywords,
12127 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12128 colorKeywords: colorKeywords,
12129 valueKeywords: valueKeywords,
12130 fontProperties: fontProperties,
12131 allowNested: true,
12132 lineComment: "//",
12133 tokenHooks: {
12134 "/": function(stream, state) {
12135 if (stream.eat("/")) {
12136 stream.skipToEnd();
12137 return ["comment", "comment"];
12138 } else if (stream.eat("*")) {
12139 state.tokenize = tokenCComment;
12140 return tokenCComment(stream, state);
12141 } else {
12142 return ["operator", "operator"];
12143 }
12144 },
12145 ":": function(stream) {
12146 if (stream.match(/\s*\{/, false))
12147 return [null, null]
12148 return false;
12149 },
12150 "$": function(stream) {
12151 stream.match(/^[\w-]+/);
12152 if (stream.match(/^\s*:/, false))
12153 return ["variable-2", "variable-definition"];
12154 return ["variable-2", "variable"];
12155 },
12156 "#": function(stream) {
12157 if (!stream.eat("{")) return false;
12158 return [null, "interpolation"];
12159 }
12160 },
12161 name: "css",
12162 helperType: "scss"
12163 });
12164
12165 CodeMirror.defineMIME("text/x-less", {
12166 mediaTypes: mediaTypes,
12167 mediaFeatures: mediaFeatures,
12168 mediaValueKeywords: mediaValueKeywords,
12169 propertyKeywords: propertyKeywords,
12170 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12171 colorKeywords: colorKeywords,
12172 valueKeywords: valueKeywords,
12173 fontProperties: fontProperties,
12174 allowNested: true,
12175 lineComment: "//",
12176 tokenHooks: {
12177 "/": function(stream, state) {
12178 if (stream.eat("/")) {
12179 stream.skipToEnd();
12180 return ["comment", "comment"];
12181 } else if (stream.eat("*")) {
12182 state.tokenize = tokenCComment;
12183 return tokenCComment(stream, state);
12184 } else {
12185 return ["operator", "operator"];
12186 }
12187 },
12188 "@": function(stream) {
12189 if (stream.eat("{")) return [null, "interpolation"];
12190 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12191 stream.eatWhile(/[\w\\\-]/);
12192 if (stream.match(/^\s*:/, false))
12193 return ["variable-2", "variable-definition"];
12194 return ["variable-2", "variable"];
12195 },
12196 "&": function() {
12197 return ["atom", "atom"];
12198 }
12199 },
12200 name: "css",
12201 helperType: "less"
12202 });
12203
12204 CodeMirror.defineMIME("text/x-gss", {
12205 documentTypes: documentTypes,
12206 mediaTypes: mediaTypes,
12207 mediaFeatures: mediaFeatures,
12208 propertyKeywords: propertyKeywords,
12209 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12210 fontProperties: fontProperties,
12211 counterDescriptors: counterDescriptors,
12212 colorKeywords: colorKeywords,
12213 valueKeywords: valueKeywords,
12214 supportsAtComponent: true,
12215 tokenHooks: {
12216 "/": function(stream, state) {
12217 if (!stream.eat("*")) return false;
12218 state.tokenize = tokenCComment;
12219 return tokenCComment(stream, state);
12220 }
12221 },
12222 name: "css",
12223 helperType: "gss"
12224 });
12225
12226 });
12227
12228 },{"../../lib/codemirror":14}],16:[function(require,module,exports){
12229 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12230 // Distributed under an MIT license: https://codemirror.net/LICENSE
12231
12232 (function(mod) {
12233 if (typeof exports == "object" && typeof module == "object") // CommonJS
12234 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
12235 else if (typeof define == "function" && define.amd) // AMD
12236 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
12237 else // Plain browser env
12238 mod(CodeMirror);
12239 })(function(CodeMirror) {
12240 "use strict";
12241
12242 var defaultTags = {
12243 script: [
12244 ["lang", /(javascript|babel)/i, "javascript"],
12245 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
12246 ["type", /./, "text/plain"],
12247 [null, null, "javascript"]
12248 ],
12249 style: [
12250 ["lang", /^css$/i, "css"],
12251 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
12252 ["type", /./, "text/plain"],
12253 [null, null, "css"]
12254 ]
12255 };
12256
12257 function maybeBackup(stream, pat, style) {
12258 var cur = stream.current(), close = cur.search(pat);
12259 if (close > -1) {
12260 stream.backUp(cur.length - close);
12261 } else if (cur.match(/<\/?$/)) {
12262 stream.backUp(cur.length);
12263 if (!stream.match(pat, false)) stream.match(cur);
12264 }
12265 return style;
12266 }
12267
12268 var attrRegexpCache = {};
12269 function getAttrRegexp(attr) {
12270 var regexp = attrRegexpCache[attr];
12271 if (regexp) return regexp;
12272 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12273 }
12274
12275 function getAttrValue(text, attr) {
12276 var match = text.match(getAttrRegexp(attr))
12277 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12278 }
12279
12280 function getTagRegexp(tagName, anchored) {
12281 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12282 }
12283
12284 function addTags(from, to) {
12285 for (var tag in from) {
12286 var dest = to[tag] || (to[tag] = []);
12287 var source = from[tag];
12288 for (var i = source.length - 1; i >= 0; i--)
12289 dest.unshift(source[i])
12290 }
12291 }
12292
12293 function findMatchingMode(tagInfo, tagText) {
12294 for (var i = 0; i < tagInfo.length; i++) {
12295 var spec = tagInfo[i];
12296 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12297 }
12298 }
12299
12300 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12301 var htmlMode = CodeMirror.getMode(config, {
12302 name: "xml",
12303 htmlMode: true,
12304 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12305 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12306 });
12307
12308 var tags = {};
12309 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12310 addTags(defaultTags, tags);
12311 if (configTags) addTags(configTags, tags);
12312 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12313 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12314
12315 function html(stream, state) {
12316 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12317 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12318 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12319 tags.hasOwnProperty(tagName)) {
12320 state.inTag = tagName + " "
12321 } else if (state.inTag && tag && />$/.test(stream.current())) {
12322 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12323 state.inTag = null
12324 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12325 var mode = CodeMirror.getMode(config, modeSpec)
12326 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12327 state.token = function (stream, state) {
12328 if (stream.match(endTagA, false)) {
12329 state.token = html;
12330 state.localState = state.localMode = null;
12331 return null;
12332 }
12333 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12334 };
12335 state.localMode = mode;
12336 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
12337 } else if (state.inTag) {
12338 state.inTag += stream.current()
12339 if (stream.eol()) state.inTag += " "
12340 }
12341 return style;
12342 };
12343
12344 return {
12345 startState: function () {
12346 var state = CodeMirror.startState(htmlMode);
12347 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12348 },
12349
12350 copyState: function (state) {
12351 var local;
12352 if (state.localState) {
12353 local = CodeMirror.copyState(state.localMode, state.localState);
12354 }
12355 return {token: state.token, inTag: state.inTag,
12356 localMode: state.localMode, localState: local,
12357 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12358 },
12359
12360 token: function (stream, state) {
12361 return state.token(stream, state);
12362 },
12363
12364 indent: function (state, textAfter, line) {
12365 if (!state.localMode || /^\s*<\//.test(textAfter))
12366 return htmlMode.indent(state.htmlState, textAfter, line);
12367 else if (state.localMode.indent)
12368 return state.localMode.indent(state.localState, textAfter, line);
12369 else
12370 return CodeMirror.Pass;
12371 },
12372
12373 innerMode: function (state) {
12374 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12375 }
12376 };
12377 }, "xml", "javascript", "css");
12378
12379 CodeMirror.defineMIME("text/html", "htmlmixed");
12380 });
12381
12382 },{"../../lib/codemirror":14,"../css/css":15,"../javascript/javascript":17,"../xml/xml":18}],17:[function(require,module,exports){
12383 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12384 // Distributed under an MIT license: https://codemirror.net/LICENSE
12385
12386 (function(mod) {
12387 if (typeof exports == "object" && typeof module == "object") // CommonJS
12388 mod(require("../../lib/codemirror"));
12389 else if (typeof define == "function" && define.amd) // AMD
12390 define(["../../lib/codemirror"], mod);
12391 else // Plain browser env
12392 mod(CodeMirror);
12393 })(function(CodeMirror) {
12394 "use strict";
12395
12396 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12397 var indentUnit = config.indentUnit;
12398 var statementIndent = parserConfig.statementIndent;
12399 var jsonldMode = parserConfig.jsonld;
12400 var jsonMode = parserConfig.json || jsonldMode;
12401 var isTS = parserConfig.typescript;
12402 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12403
12404 // Tokenizer
12405
12406 var keywords = function(){
12407 function kw(type) {return {type: type, style: "keyword"};}
12408 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12409 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12410
12411 return {
12412 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12413 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12414 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12415 "function": kw("function"), "catch": kw("catch"),
12416 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12417 "in": operator, "typeof": operator, "instanceof": operator,
12418 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12419 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12420 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12421 "await": C
12422 };
12423 }();
12424
12425 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12426 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12427
12428 function readRegexp(stream) {
12429 var escaped = false, next, inSet = false;
12430 while ((next = stream.next()) != null) {
12431 if (!escaped) {
12432 if (next == "/" && !inSet) return;
12433 if (next == "[") inSet = true;
12434 else if (inSet && next == "]") inSet = false;
12435 }
12436 escaped = !escaped && next == "\\";
12437 }
12438 }
12439
12440 // Used as scratch variables to communicate multiple values without
12441 // consing up tons of objects.
12442 var type, content;
12443 function ret(tp, style, cont) {
12444 type = tp; content = cont;
12445 return style;
12446 }
12447 function tokenBase(stream, state) {
12448 var ch = stream.next();
12449 if (ch == '"' || ch == "'") {
12450 state.tokenize = tokenString(ch);
12451 return state.tokenize(stream, state);
12452 } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
12453 return ret("number", "number");
12454 } else if (ch == "." && stream.match("..")) {
12455 return ret("spread", "meta");
12456 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12457 return ret(ch);
12458 } else if (ch == "=" && stream.eat(">")) {
12459 return ret("=>", "operator");
12460 } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
12461 return ret("number", "number");
12462 } else if (/\d/.test(ch)) {
12463 stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
12464 return ret("number", "number");
12465 } else if (ch == "/") {
12466 if (stream.eat("*")) {
12467 state.tokenize = tokenComment;
12468 return tokenComment(stream, state);
12469 } else if (stream.eat("/")) {
12470 stream.skipToEnd();
12471 return ret("comment", "comment");
12472 } else if (expressionAllowed(stream, state, 1)) {
12473 readRegexp(stream);
12474 stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
12475 return ret("regexp", "string-2");
12476 } else {
12477 stream.eat("=");
12478 return ret("operator", "operator", stream.current());
12479 }
12480 } else if (ch == "`") {
12481 state.tokenize = tokenQuasi;
12482 return tokenQuasi(stream, state);
12483 } else if (ch == "#") {
12484 stream.skipToEnd();
12485 return ret("error", "error");
12486 } else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) {
12487 stream.skipToEnd()
12488 return ret("comment", "comment")
12489 } else if (isOperatorChar.test(ch)) {
12490 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12491 if (stream.eat("=")) {
12492 if (ch == "!" || ch == "=") stream.eat("=")
12493 } else if (/[<>*+\-]/.test(ch)) {
12494 stream.eat(ch)
12495 if (ch == ">") stream.eat(ch)
12496 }
12497 }
12498 return ret("operator", "operator", stream.current());
12499 } else if (wordRE.test(ch)) {
12500 stream.eatWhile(wordRE);
12501 var word = stream.current()
12502 if (state.lastType != ".") {
12503 if (keywords.propertyIsEnumerable(word)) {
12504 var kw = keywords[word]
12505 return ret(kw.type, kw.style, word)
12506 }
12507 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
12508 return ret("async", "keyword", word)
12509 }
12510 return ret("variable", "variable", word)
12511 }
12512 }
12513
12514 function tokenString(quote) {
12515 return function(stream, state) {
12516 var escaped = false, next;
12517 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12518 state.tokenize = tokenBase;
12519 return ret("jsonld-keyword", "meta");
12520 }
12521 while ((next = stream.next()) != null) {
12522 if (next == quote && !escaped) break;
12523 escaped = !escaped && next == "\\";
12524 }
12525 if (!escaped) state.tokenize = tokenBase;
12526 return ret("string", "string");
12527 };
12528 }
12529
12530 function tokenComment(stream, state) {
12531 var maybeEnd = false, ch;
12532 while (ch = stream.next()) {
12533 if (ch == "/" && maybeEnd) {
12534 state.tokenize = tokenBase;
12535 break;
12536 }
12537 maybeEnd = (ch == "*");
12538 }
12539 return ret("comment", "comment");
12540 }
12541
12542 function tokenQuasi(stream, state) {
12543 var escaped = false, next;
12544 while ((next = stream.next()) != null) {
12545 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12546 state.tokenize = tokenBase;
12547 break;
12548 }
12549 escaped = !escaped && next == "\\";
12550 }
12551 return ret("quasi", "string-2", stream.current());
12552 }
12553
12554 var brackets = "([{}])";
12555 // This is a crude lookahead trick to try and notice that we're
12556 // parsing the argument patterns for a fat-arrow function before we
12557 // actually hit the arrow token. It only works if the arrow is on
12558 // the same line as the arguments and there's no strange noise
12559 // (comments) in between. Fallback is to only notice when we hit the
12560 // arrow, and not declare the arguments as locals for the arrow
12561 // body.
12562 function findFatArrow(stream, state) {
12563 if (state.fatArrowAt) state.fatArrowAt = null;
12564 var arrow = stream.string.indexOf("=>", stream.start);
12565 if (arrow < 0) return;
12566
12567 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12568 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12569 if (m) arrow = m.index
12570 }
12571
12572 var depth = 0, sawSomething = false;
12573 for (var pos = arrow - 1; pos >= 0; --pos) {
12574 var ch = stream.string.charAt(pos);
12575 var bracket = brackets.indexOf(ch);
12576 if (bracket >= 0 && bracket < 3) {
12577 if (!depth) { ++pos; break; }
12578 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12579 } else if (bracket >= 3 && bracket < 6) {
12580 ++depth;
12581 } else if (wordRE.test(ch)) {
12582 sawSomething = true;
12583 } else if (/["'\/`]/.test(ch)) {
12584 for (;; --pos) {
12585 if (pos == 0) return
12586 var next = stream.string.charAt(pos - 1)
12587 if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
12588 }
12589 } else if (sawSomething && !depth) {
12590 ++pos;
12591 break;
12592 }
12593 }
12594 if (sawSomething && !depth) state.fatArrowAt = pos;
12595 }
12596
12597 // Parser
12598
12599 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12600
12601 function JSLexical(indented, column, type, align, prev, info) {
12602 this.indented = indented;
12603 this.column = column;
12604 this.type = type;
12605 this.prev = prev;
12606 this.info = info;
12607 if (align != null) this.align = align;
12608 }
12609
12610 function inScope(state, varname) {
12611 for (var v = state.localVars; v; v = v.next)
12612 if (v.name == varname) return true;
12613 for (var cx = state.context; cx; cx = cx.prev) {
12614 for (var v = cx.vars; v; v = v.next)
12615 if (v.name == varname) return true;
12616 }
12617 }
12618
12619 function parseJS(state, style, type, content, stream) {
12620 var cc = state.cc;
12621 // Communicate our context to the combinators.
12622 // (Less wasteful than consing up a hundred closures on every call.)
12623 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12624
12625 if (!state.lexical.hasOwnProperty("align"))
12626 state.lexical.align = true;
12627
12628 while(true) {
12629 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12630 if (combinator(type, content)) {
12631 while(cc.length && cc[cc.length - 1].lex)
12632 cc.pop()();
12633 if (cx.marked) return cx.marked;
12634 if (type == "variable" && inScope(state, content)) return "variable-2";
12635 return style;
12636 }
12637 }
12638 }
12639
12640 // Combinator utils
12641
12642 var cx = {state: null, column: null, marked: null, cc: null};
12643 function pass() {
12644 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12645 }
12646 function cont() {
12647 pass.apply(null, arguments);
12648 return true;
12649 }
12650 function inList(name, list) {
12651 for (var v = list; v; v = v.next) if (v.name == name) return true
12652 return false;
12653 }
12654 function register(varname) {
12655 var state = cx.state;
12656 cx.marked = "def";
12657 if (state.context) {
12658 if (state.lexical.info == "var" && state.context && state.context.block) {
12659 // FIXME function decls are also not block scoped
12660 var newContext = registerVarScoped(varname, state.context)
12661 if (newContext != null) {
12662 state.context = newContext
12663 return
12664 }
12665 } else if (!inList(varname, state.localVars)) {
12666 state.localVars = new Var(varname, state.localVars)
12667 return
12668 }
12669 }
12670 // Fall through means this is global
12671 if (parserConfig.globalVars && !inList(varname, state.globalVars))
12672 state.globalVars = new Var(varname, state.globalVars)
12673 }
12674 function registerVarScoped(varname, context) {
12675 if (!context) {
12676 return null
12677 } else if (context.block) {
12678 var inner = registerVarScoped(varname, context.prev)
12679 if (!inner) return null
12680 if (inner == context.prev) return context
12681 return new Context(inner, context.vars, true)
12682 } else if (inList(varname, context.vars)) {
12683 return context
12684 } else {
12685 return new Context(context.prev, new Var(varname, context.vars), false)
12686 }
12687 }
12688
12689 function isModifier(name) {
12690 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12691 }
12692
12693 // Combinators
12694
12695 function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
12696 function Var(name, next) { this.name = name; this.next = next }
12697
12698 var defaultVars = new Var("this", new Var("arguments", null))
12699 function pushcontext() {
12700 cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
12701 cx.state.localVars = defaultVars
12702 }
12703 function pushblockcontext() {
12704 cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
12705 cx.state.localVars = null
12706 }
12707 function popcontext() {
12708 cx.state.localVars = cx.state.context.vars
12709 cx.state.context = cx.state.context.prev
12710 }
12711 popcontext.lex = true
12712 function pushlex(type, info) {
12713 var result = function() {
12714 var state = cx.state, indent = state.indented;
12715 if (state.lexical.type == "stat") indent = state.lexical.indented;
12716 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12717 indent = outer.indented;
12718 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12719 };
12720 result.lex = true;
12721 return result;
12722 }
12723 function poplex() {
12724 var state = cx.state;
12725 if (state.lexical.prev) {
12726 if (state.lexical.type == ")")
12727 state.indented = state.lexical.indented;
12728 state.lexical = state.lexical.prev;
12729 }
12730 }
12731 poplex.lex = true;
12732
12733 function expect(wanted) {
12734 function exp(type) {
12735 if (type == wanted) return cont();
12736 else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
12737 else return cont(exp);
12738 };
12739 return exp;
12740 }
12741
12742 function statement(type, value) {
12743 if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
12744 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12745 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12746 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12747 if (type == "debugger") return cont(expect(";"));
12748 if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
12749 if (type == ";") return cont();
12750 if (type == "if") {
12751 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12752 cx.state.cc.pop()();
12753 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12754 }
12755 if (type == "function") return cont(functiondef);
12756 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12757 if (type == "class" || (isTS && value == "interface")) {
12758 cx.marked = "keyword"
12759 return cont(pushlex("form", type == "class" ? type : value), className, poplex)
12760 }
12761 if (type == "variable") {
12762 if (isTS && value == "declare") {
12763 cx.marked = "keyword"
12764 return cont(statement)
12765 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12766 cx.marked = "keyword"
12767 if (value == "enum") return cont(enumdef);
12768 else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
12769 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12770 } else if (isTS && value == "namespace") {
12771 cx.marked = "keyword"
12772 return cont(pushlex("form"), expression, statement, poplex)
12773 } else if (isTS && value == "abstract") {
12774 cx.marked = "keyword"
12775 return cont(statement)
12776 } else {
12777 return cont(pushlex("stat"), maybelabel);
12778 }
12779 }
12780 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
12781 block, poplex, poplex, popcontext);
12782 if (type == "case") return cont(expression, expect(":"));
12783 if (type == "default") return cont(expect(":"));
12784 if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
12785 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12786 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12787 if (type == "async") return cont(statement)
12788 if (value == "@") return cont(expression, statement)
12789 return pass(pushlex("stat"), expression, expect(";"), poplex);
12790 }
12791 function maybeCatchBinding(type) {
12792 if (type == "(") return cont(funarg, expect(")"))
12793 }
12794 function expression(type, value) {
12795 return expressionInner(type, value, false);
12796 }
12797 function expressionNoComma(type, value) {
12798 return expressionInner(type, value, true);
12799 }
12800 function parenExpr(type) {
12801 if (type != "(") return pass()
12802 return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
12803 }
12804 function expressionInner(type, value, noComma) {
12805 if (cx.state.fatArrowAt == cx.stream.start) {
12806 var body = noComma ? arrowBodyNoComma : arrowBody;
12807 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12808 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12809 }
12810
12811 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12812 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12813 if (type == "function") return cont(functiondef, maybeop);
12814 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12815 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12816 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12817 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12818 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12819 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12820 if (type == "quasi") return pass(quasi, maybeop);
12821 if (type == "new") return cont(maybeTarget(noComma));
12822 if (type == "import") return cont(expression);
12823 return cont();
12824 }
12825 function maybeexpression(type) {
12826 if (type.match(/[;\}\)\],]/)) return pass();
12827 return pass(expression);
12828 }
12829
12830 function maybeoperatorComma(type, value) {
12831 if (type == ",") return cont(maybeexpression);
12832 return maybeoperatorNoComma(type, value, false);
12833 }
12834 function maybeoperatorNoComma(type, value, noComma) {
12835 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12836 var expr = noComma == false ? expression : expressionNoComma;
12837 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12838 if (type == "operator") {
12839 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12840 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12841 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12842 if (value == "?") return cont(expression, expect(":"), expr);
12843 return cont(expr);
12844 }
12845 if (type == "quasi") { return pass(quasi, me); }
12846 if (type == ";") return;
12847 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12848 if (type == ".") return cont(property, me);
12849 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12850 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12851 if (type == "regexp") {
12852 cx.state.lastType = cx.marked = "operator"
12853 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12854 return cont(expr)
12855 }
12856 }
12857 function quasi(type, value) {
12858 if (type != "quasi") return pass();
12859 if (value.slice(value.length - 2) != "${") return cont(quasi);
12860 return cont(expression, continueQuasi);
12861 }
12862 function continueQuasi(type) {
12863 if (type == "}") {
12864 cx.marked = "string-2";
12865 cx.state.tokenize = tokenQuasi;
12866 return cont(quasi);
12867 }
12868 }
12869 function arrowBody(type) {
12870 findFatArrow(cx.stream, cx.state);
12871 return pass(type == "{" ? statement : expression);
12872 }
12873 function arrowBodyNoComma(type) {
12874 findFatArrow(cx.stream, cx.state);
12875 return pass(type == "{" ? statement : expressionNoComma);
12876 }
12877 function maybeTarget(noComma) {
12878 return function(type) {
12879 if (type == ".") return cont(noComma ? targetNoComma : target);
12880 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12881 else return pass(noComma ? expressionNoComma : expression);
12882 };
12883 }
12884 function target(_, value) {
12885 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12886 }
12887 function targetNoComma(_, value) {
12888 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12889 }
12890 function maybelabel(type) {
12891 if (type == ":") return cont(poplex, statement);
12892 return pass(maybeoperatorComma, expect(";"), poplex);
12893 }
12894 function property(type) {
12895 if (type == "variable") {cx.marked = "property"; return cont();}
12896 }
12897 function objprop(type, value) {
12898 if (type == "async") {
12899 cx.marked = "property";
12900 return cont(objprop);
12901 } else if (type == "variable" || cx.style == "keyword") {
12902 cx.marked = "property";
12903 if (value == "get" || value == "set") return cont(getterSetter);
12904 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12905 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12906 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12907 return cont(afterprop);
12908 } else if (type == "number" || type == "string") {
12909 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12910 return cont(afterprop);
12911 } else if (type == "jsonld-keyword") {
12912 return cont(afterprop);
12913 } else if (isTS && isModifier(value)) {
12914 cx.marked = "keyword"
12915 return cont(objprop)
12916 } else if (type == "[") {
12917 return cont(expression, maybetype, expect("]"), afterprop);
12918 } else if (type == "spread") {
12919 return cont(expressionNoComma, afterprop);
12920 } else if (value == "*") {
12921 cx.marked = "keyword";
12922 return cont(objprop);
12923 } else if (type == ":") {
12924 return pass(afterprop)
12925 }
12926 }
12927 function getterSetter(type) {
12928 if (type != "variable") return pass(afterprop);
12929 cx.marked = "property";
12930 return cont(functiondef);
12931 }
12932 function afterprop(type) {
12933 if (type == ":") return cont(expressionNoComma);
12934 if (type == "(") return pass(functiondef);
12935 }
12936 function commasep(what, end, sep) {
12937 function proceed(type, value) {
12938 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12939 var lex = cx.state.lexical;
12940 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12941 return cont(function(type, value) {
12942 if (type == end || value == end) return pass()
12943 return pass(what)
12944 }, proceed);
12945 }
12946 if (type == end || value == end) return cont();
12947 if (sep && sep.indexOf(";") > -1) return pass(what)
12948 return cont(expect(end));
12949 }
12950 return function(type, value) {
12951 if (type == end || value == end) return cont();
12952 return pass(what, proceed);
12953 };
12954 }
12955 function contCommasep(what, end, info) {
12956 for (var i = 3; i < arguments.length; i++)
12957 cx.cc.push(arguments[i]);
12958 return cont(pushlex(end, info), commasep(what, end), poplex);
12959 }
12960 function block(type) {
12961 if (type == "}") return cont();
12962 return pass(statement, block);
12963 }
12964 function maybetype(type, value) {
12965 if (isTS) {
12966 if (type == ":") return cont(typeexpr);
12967 if (value == "?") return cont(maybetype);
12968 }
12969 }
12970 function maybetypeOrIn(type, value) {
12971 if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
12972 }
12973 function mayberettype(type) {
12974 if (isTS && type == ":") {
12975 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12976 else return cont(typeexpr)
12977 }
12978 }
12979 function isKW(_, value) {
12980 if (value == "is") {
12981 cx.marked = "keyword"
12982 return cont()
12983 }
12984 }
12985 function typeexpr(type, value) {
12986 if (value == "keyof" || value == "typeof" || value == "infer") {
12987 cx.marked = "keyword"
12988 return cont(value == "typeof" ? expressionNoComma : typeexpr)
12989 }
12990 if (type == "variable" || value == "void") {
12991 cx.marked = "type"
12992 return cont(afterType)
12993 }
12994 if (value == "|" || value == "&") return cont(typeexpr)
12995 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12996 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12997 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12998 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
12999 if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
13000 }
13001 function maybeReturnType(type) {
13002 if (type == "=>") return cont(typeexpr)
13003 }
13004 function typeprop(type, value) {
13005 if (type == "variable" || cx.style == "keyword") {
13006 cx.marked = "property"
13007 return cont(typeprop)
13008 } else if (value == "?" || type == "number" || type == "string") {
13009 return cont(typeprop)
13010 } else if (type == ":") {
13011 return cont(typeexpr)
13012 } else if (type == "[") {
13013 return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
13014 } else if (type == "(") {
13015 return pass(functiondecl, typeprop)
13016 }
13017 }
13018 function typearg(type, value) {
13019 if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
13020 if (type == ":") return cont(typeexpr)
13021 if (type == "spread") return cont(typearg)
13022 return pass(typeexpr)
13023 }
13024 function afterType(type, value) {
13025 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
13026 if (value == "|" || type == "." || value == "&") return cont(typeexpr)
13027 if (type == "[") return cont(typeexpr, expect("]"), afterType)
13028 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
13029 if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
13030 }
13031 function maybeTypeArgs(_, value) {
13032 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
13033 }
13034 function typeparam() {
13035 return pass(typeexpr, maybeTypeDefault)
13036 }
13037 function maybeTypeDefault(_, value) {
13038 if (value == "=") return cont(typeexpr)
13039 }
13040 function vardef(_, value) {
13041 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
13042 return pass(pattern, maybetype, maybeAssign, vardefCont);
13043 }
13044 function pattern(type, value) {
13045 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
13046 if (type == "variable") { register(value); return cont(); }
13047 if (type == "spread") return cont(pattern);
13048 if (type == "[") return contCommasep(eltpattern, "]");
13049 if (type == "{") return contCommasep(proppattern, "}");
13050 }
13051 function proppattern(type, value) {
13052 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
13053 register(value);
13054 return cont(maybeAssign);
13055 }
13056 if (type == "variable") cx.marked = "property";
13057 if (type == "spread") return cont(pattern);
13058 if (type == "}") return pass();
13059 if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
13060 return cont(expect(":"), pattern, maybeAssign);
13061 }
13062 function eltpattern() {
13063 return pass(pattern, maybeAssign)
13064 }
13065 function maybeAssign(_type, value) {
13066 if (value == "=") return cont(expressionNoComma);
13067 }
13068 function vardefCont(type) {
13069 if (type == ",") return cont(vardef);
13070 }
13071 function maybeelse(type, value) {
13072 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
13073 }
13074 function forspec(type, value) {
13075 if (value == "await") return cont(forspec);
13076 if (type == "(") return cont(pushlex(")"), forspec1, poplex);
13077 }
13078 function forspec1(type) {
13079 if (type == "var") return cont(vardef, forspec2);
13080 if (type == "variable") return cont(forspec2);
13081 return pass(forspec2)
13082 }
13083 function forspec2(type, value) {
13084 if (type == ")") return cont()
13085 if (type == ";") return cont(forspec2)
13086 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
13087 return pass(expression, forspec2)
13088 }
13089 function functiondef(type, value) {
13090 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
13091 if (type == "variable") {register(value); return cont(functiondef);}
13092 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
13093 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
13094 }
13095 function functiondecl(type, value) {
13096 if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
13097 if (type == "variable") {register(value); return cont(functiondecl);}
13098 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
13099 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
13100 }
13101 function typename(type, value) {
13102 if (type == "keyword" || type == "variable") {
13103 cx.marked = "type"
13104 return cont(typename)
13105 } else if (value == "<") {
13106 return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
13107 }
13108 }
13109 function funarg(type, value) {
13110 if (value == "@") cont(expression, funarg)
13111 if (type == "spread") return cont(funarg);
13112 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
13113 if (isTS && type == "this") return cont(maybetype, maybeAssign)
13114 return pass(pattern, maybetype, maybeAssign);
13115 }
13116 function classExpression(type, value) {
13117 // Class expressions may have an optional name.
13118 if (type == "variable") return className(type, value);
13119 return classNameAfter(type, value);
13120 }
13121 function className(type, value) {
13122 if (type == "variable") {register(value); return cont(classNameAfter);}
13123 }
13124 function classNameAfter(type, value) {
13125 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
13126 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
13127 if (value == "implements") cx.marked = "keyword";
13128 return cont(isTS ? typeexpr : expression, classNameAfter);
13129 }
13130 if (type == "{") return cont(pushlex("}"), classBody, poplex);
13131 }
13132 function classBody(type, value) {
13133 if (type == "async" ||
13134 (type == "variable" &&
13135 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
13136 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
13137 cx.marked = "keyword";
13138 return cont(classBody);
13139 }
13140 if (type == "variable" || cx.style == "keyword") {
13141 cx.marked = "property";
13142 return cont(isTS ? classfield : functiondef, classBody);
13143 }
13144 if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
13145 if (type == "[")
13146 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
13147 if (value == "*") {
13148 cx.marked = "keyword";
13149 return cont(classBody);
13150 }
13151 if (isTS && type == "(") return pass(functiondecl, classBody)
13152 if (type == ";" || type == ",") return cont(classBody);
13153 if (type == "}") return cont();
13154 if (value == "@") return cont(expression, classBody)
13155 }
13156 function classfield(type, value) {
13157 if (value == "?") return cont(classfield)
13158 if (type == ":") return cont(typeexpr, maybeAssign)
13159 if (value == "=") return cont(expressionNoComma)
13160 var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
13161 return pass(isInterface ? functiondecl : functiondef)
13162 }
13163 function afterExport(type, value) {
13164 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
13165 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
13166 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
13167 return pass(statement);
13168 }
13169 function exportField(type, value) {
13170 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
13171 if (type == "variable") return pass(expressionNoComma, exportField);
13172 }
13173 function afterImport(type) {
13174 if (type == "string") return cont();
13175 if (type == "(") return pass(expression);
13176 return pass(importSpec, maybeMoreImports, maybeFrom);
13177 }
13178 function importSpec(type, value) {
13179 if (type == "{") return contCommasep(importSpec, "}");
13180 if (type == "variable") register(value);
13181 if (value == "*") cx.marked = "keyword";
13182 return cont(maybeAs);
13183 }
13184 function maybeMoreImports(type) {
13185 if (type == ",") return cont(importSpec, maybeMoreImports)
13186 }
13187 function maybeAs(_type, value) {
13188 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
13189 }
13190 function maybeFrom(_type, value) {
13191 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
13192 }
13193 function arrayLiteral(type) {
13194 if (type == "]") return cont();
13195 return pass(commasep(expressionNoComma, "]"));
13196 }
13197 function enumdef() {
13198 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
13199 }
13200 function enummember() {
13201 return pass(pattern, maybeAssign);
13202 }
13203
13204 function isContinuedStatement(state, textAfter) {
13205 return state.lastType == "operator" || state.lastType == "," ||
13206 isOperatorChar.test(textAfter.charAt(0)) ||
13207 /[,.]/.test(textAfter.charAt(0));
13208 }
13209
13210 function expressionAllowed(stream, state, backUp) {
13211 return state.tokenize == tokenBase &&
13212 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
13213 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
13214 }
13215
13216 // Interface
13217
13218 return {
13219 startState: function(basecolumn) {
13220 var state = {
13221 tokenize: tokenBase,
13222 lastType: "sof",
13223 cc: [],
13224 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
13225 localVars: parserConfig.localVars,
13226 context: parserConfig.localVars && new Context(null, null, false),
13227 indented: basecolumn || 0
13228 };
13229 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
13230 state.globalVars = parserConfig.globalVars;
13231 return state;
13232 },
13233
13234 token: function(stream, state) {
13235 if (stream.sol()) {
13236 if (!state.lexical.hasOwnProperty("align"))
13237 state.lexical.align = false;
13238 state.indented = stream.indentation();
13239 findFatArrow(stream, state);
13240 }
13241 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
13242 var style = state.tokenize(stream, state);
13243 if (type == "comment") return style;
13244 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
13245 return parseJS(state, style, type, content, stream);
13246 },
13247
13248 indent: function(state, textAfter) {
13249 if (state.tokenize == tokenComment) return CodeMirror.Pass;
13250 if (state.tokenize != tokenBase) return 0;
13251 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
13252 // Kludge to prevent 'maybelse' from blocking lexical scope pops
13253 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
13254 var c = state.cc[i];
13255 if (c == poplex) lexical = lexical.prev;
13256 else if (c != maybeelse) break;
13257 }
13258 while ((lexical.type == "stat" || lexical.type == "form") &&
13259 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
13260 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
13261 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
13262 lexical = lexical.prev;
13263 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
13264 lexical = lexical.prev;
13265 var type = lexical.type, closing = firstChar == type;
13266
13267 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
13268 else if (type == "form" && firstChar == "{") return lexical.indented;
13269 else if (type == "form") return lexical.indented + indentUnit;
13270 else if (type == "stat")
13271 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
13272 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
13273 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
13274 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
13275 else return lexical.indented + (closing ? 0 : indentUnit);
13276 },
13277
13278 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
13279 blockCommentStart: jsonMode ? null : "/*",
13280 blockCommentEnd: jsonMode ? null : "*/",
13281 blockCommentContinue: jsonMode ? null : " * ",
13282 lineComment: jsonMode ? null : "//",
13283 fold: "brace",
13284 closeBrackets: "()[]{}''\"\"``",
13285
13286 helperType: jsonMode ? "json" : "javascript",
13287 jsonldMode: jsonldMode,
13288 jsonMode: jsonMode,
13289
13290 expressionAllowed: expressionAllowed,
13291
13292 skipExpression: function(state) {
13293 var top = state.cc[state.cc.length - 1]
13294 if (top == expression || top == expressionNoComma) state.cc.pop()
13295 }
13296 };
13297 });
13298
13299 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
13300
13301 CodeMirror.defineMIME("text/javascript", "javascript");
13302 CodeMirror.defineMIME("text/ecmascript", "javascript");
13303 CodeMirror.defineMIME("application/javascript", "javascript");
13304 CodeMirror.defineMIME("application/x-javascript", "javascript");
13305 CodeMirror.defineMIME("application/ecmascript", "javascript");
13306 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
13307 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
13308 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
13309 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
13310 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
13311
13312 });
13313
13314 },{"../../lib/codemirror":14}],18:[function(require,module,exports){
13315 // CodeMirror, copyright (c) by Marijn Haverbeke and others
13316 // Distributed under an MIT license: https://codemirror.net/LICENSE
13317
13318 (function(mod) {
13319 if (typeof exports == "object" && typeof module == "object") // CommonJS
13320 mod(require("../../lib/codemirror"));
13321 else if (typeof define == "function" && define.amd) // AMD
13322 define(["../../lib/codemirror"], mod);
13323 else // Plain browser env
13324 mod(CodeMirror);
13325 })(function(CodeMirror) {
13326 "use strict";
13327
13328 var htmlConfig = {
13329 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13330 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13331 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13332 'track': true, 'wbr': true, 'menuitem': true},
13333 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13334 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13335 'th': true, 'tr': true},
13336 contextGrabbers: {
13337 'dd': {'dd': true, 'dt': true},
13338 'dt': {'dd': true, 'dt': true},
13339 'li': {'li': true},
13340 'option': {'option': true, 'optgroup': true},
13341 'optgroup': {'optgroup': true},
13342 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13343 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13344 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13345 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13346 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13347 'rp': {'rp': true, 'rt': true},
13348 'rt': {'rp': true, 'rt': true},
13349 'tbody': {'tbody': true, 'tfoot': true},
13350 'td': {'td': true, 'th': true},
13351 'tfoot': {'tbody': true},
13352 'th': {'td': true, 'th': true},
13353 'thead': {'tbody': true, 'tfoot': true},
13354 'tr': {'tr': true}
13355 },
13356 doNotIndent: {"pre": true},
13357 allowUnquoted: true,
13358 allowMissing: true,
13359 caseFold: true
13360 }
13361
13362 var xmlConfig = {
13363 autoSelfClosers: {},
13364 implicitlyClosed: {},
13365 contextGrabbers: {},
13366 doNotIndent: {},
13367 allowUnquoted: false,
13368 allowMissing: false,
13369 allowMissingTagName: false,
13370 caseFold: false
13371 }
13372
13373 CodeMirror.defineMode("xml", function(editorConf, config_) {
13374 var indentUnit = editorConf.indentUnit
13375 var config = {}
13376 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13377 for (var prop in defaults) config[prop] = defaults[prop]
13378 for (var prop in config_) config[prop] = config_[prop]
13379
13380 // Return variables for tokenizers
13381 var type, setStyle;
13382
13383 function inText(stream, state) {
13384 function chain(parser) {
13385 state.tokenize = parser;
13386 return parser(stream, state);
13387 }
13388
13389 var ch = stream.next();
13390 if (ch == "<") {
13391 if (stream.eat("!")) {
13392 if (stream.eat("[")) {
13393 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13394 else return null;
13395 } else if (stream.match("--")) {
13396 return chain(inBlock("comment", "-->"));
13397 } else if (stream.match("DOCTYPE", true, true)) {
13398 stream.eatWhile(/[\w\._\-]/);
13399 return chain(doctype(1));
13400 } else {
13401 return null;
13402 }
13403 } else if (stream.eat("?")) {
13404 stream.eatWhile(/[\w\._\-]/);
13405 state.tokenize = inBlock("meta", "?>");
13406 return "meta";
13407 } else {
13408 type = stream.eat("/") ? "closeTag" : "openTag";
13409 state.tokenize = inTag;
13410 return "tag bracket";
13411 }
13412 } else if (ch == "&") {
13413 var ok;
13414 if (stream.eat("#")) {
13415 if (stream.eat("x")) {
13416 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13417 } else {
13418 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13419 }
13420 } else {
13421 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13422 }
13423 return ok ? "atom" : "error";
13424 } else {
13425 stream.eatWhile(/[^&<]/);
13426 return null;
13427 }
13428 }
13429 inText.isInText = true;
13430
13431 function inTag(stream, state) {
13432 var ch = stream.next();
13433 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13434 state.tokenize = inText;
13435 type = ch == ">" ? "endTag" : "selfcloseTag";
13436 return "tag bracket";
13437 } else if (ch == "=") {
13438 type = "equals";
13439 return null;
13440 } else if (ch == "<") {
13441 state.tokenize = inText;
13442 state.state = baseState;
13443 state.tagName = state.tagStart = null;
13444 var next = state.tokenize(stream, state);
13445 return next ? next + " tag error" : "tag error";
13446 } else if (/[\'\"]/.test(ch)) {
13447 state.tokenize = inAttribute(ch);
13448 state.stringStartCol = stream.column();
13449 return state.tokenize(stream, state);
13450 } else {
13451 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13452 return "word";
13453 }
13454 }
13455
13456 function inAttribute(quote) {
13457 var closure = function(stream, state) {
13458 while (!stream.eol()) {
13459 if (stream.next() == quote) {
13460 state.tokenize = inTag;
13461 break;
13462 }
13463 }
13464 return "string";
13465 };
13466 closure.isInAttribute = true;
13467 return closure;
13468 }
13469
13470 function inBlock(style, terminator) {
13471 return function(stream, state) {
13472 while (!stream.eol()) {
13473 if (stream.match(terminator)) {
13474 state.tokenize = inText;
13475 break;
13476 }
13477 stream.next();
13478 }
13479 return style;
13480 }
13481 }
13482
13483 function doctype(depth) {
13484 return function(stream, state) {
13485 var ch;
13486 while ((ch = stream.next()) != null) {
13487 if (ch == "<") {
13488 state.tokenize = doctype(depth + 1);
13489 return state.tokenize(stream, state);
13490 } else if (ch == ">") {
13491 if (depth == 1) {
13492 state.tokenize = inText;
13493 break;
13494 } else {
13495 state.tokenize = doctype(depth - 1);
13496 return state.tokenize(stream, state);
13497 }
13498 }
13499 }
13500 return "meta";
13501 };
13502 }
13503
13504 function Context(state, tagName, startOfLine) {
13505 this.prev = state.context;
13506 this.tagName = tagName;
13507 this.indent = state.indented;
13508 this.startOfLine = startOfLine;
13509 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13510 this.noIndent = true;
13511 }
13512 function popContext(state) {
13513 if (state.context) state.context = state.context.prev;
13514 }
13515 function maybePopContext(state, nextTagName) {
13516 var parentTagName;
13517 while (true) {
13518 if (!state.context) {
13519 return;
13520 }
13521 parentTagName = state.context.tagName;
13522 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13523 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13524 return;
13525 }
13526 popContext(state);
13527 }
13528 }
13529
13530 function baseState(type, stream, state) {
13531 if (type == "openTag") {
13532 state.tagStart = stream.column();
13533 return tagNameState;
13534 } else if (type == "closeTag") {
13535 return closeTagNameState;
13536 } else {
13537 return baseState;
13538 }
13539 }
13540 function tagNameState(type, stream, state) {
13541 if (type == "word") {
13542 state.tagName = stream.current();
13543 setStyle = "tag";
13544 return attrState;
13545 } else if (config.allowMissingTagName && type == "endTag") {
13546 setStyle = "tag bracket";
13547 return attrState(type, stream, state);
13548 } else {
13549 setStyle = "error";
13550 return tagNameState;
13551 }
13552 }
13553 function closeTagNameState(type, stream, state) {
13554 if (type == "word") {
13555 var tagName = stream.current();
13556 if (state.context && state.context.tagName != tagName &&
13557 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13558 popContext(state);
13559 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13560 setStyle = "tag";
13561 return closeState;
13562 } else {
13563 setStyle = "tag error";
13564 return closeStateErr;
13565 }
13566 } else if (config.allowMissingTagName && type == "endTag") {
13567 setStyle = "tag bracket";
13568 return closeState(type, stream, state);
13569 } else {
13570 setStyle = "error";
13571 return closeStateErr;
13572 }
13573 }
13574
13575 function closeState(type, _stream, state) {
13576 if (type != "endTag") {
13577 setStyle = "error";
13578 return closeState;
13579 }
13580 popContext(state);
13581 return baseState;
13582 }
13583 function closeStateErr(type, stream, state) {
13584 setStyle = "error";
13585 return closeState(type, stream, state);
13586 }
13587
13588 function attrState(type, _stream, state) {
13589 if (type == "word") {
13590 setStyle = "attribute";
13591 return attrEqState;
13592 } else if (type == "endTag" || type == "selfcloseTag") {
13593 var tagName = state.tagName, tagStart = state.tagStart;
13594 state.tagName = state.tagStart = null;
13595 if (type == "selfcloseTag" ||
13596 config.autoSelfClosers.hasOwnProperty(tagName)) {
13597 maybePopContext(state, tagName);
13598 } else {
13599 maybePopContext(state, tagName);
13600 state.context = new Context(state, tagName, tagStart == state.indented);
13601 }
13602 return baseState;
13603 }
13604 setStyle = "error";
13605 return attrState;
13606 }
13607 function attrEqState(type, stream, state) {
13608 if (type == "equals") return attrValueState;
13609 if (!config.allowMissing) setStyle = "error";
13610 return attrState(type, stream, state);
13611 }
13612 function attrValueState(type, stream, state) {
13613 if (type == "string") return attrContinuedState;
13614 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13615 setStyle = "error";
13616 return attrState(type, stream, state);
13617 }
13618 function attrContinuedState(type, stream, state) {
13619 if (type == "string") return attrContinuedState;
13620 return attrState(type, stream, state);
13621 }
13622
13623 return {
13624 startState: function(baseIndent) {
13625 var state = {tokenize: inText,
13626 state: baseState,
13627 indented: baseIndent || 0,
13628 tagName: null, tagStart: null,
13629 context: null}
13630 if (baseIndent != null) state.baseIndent = baseIndent
13631 return state
13632 },
13633
13634 token: function(stream, state) {
13635 if (!state.tagName && stream.sol())
13636 state.indented = stream.indentation();
13637
13638 if (stream.eatSpace()) return null;
13639 type = null;
13640 var style = state.tokenize(stream, state);
13641 if ((style || type) && style != "comment") {
13642 setStyle = null;
13643 state.state = state.state(type || style, stream, state);
13644 if (setStyle)
13645 style = setStyle == "error" ? style + " error" : setStyle;
13646 }
13647 return style;
13648 },
13649
13650 indent: function(state, textAfter, fullLine) {
13651 var context = state.context;
13652 // Indent multi-line strings (e.g. css).
13653 if (state.tokenize.isInAttribute) {
13654 if (state.tagStart == state.indented)
13655 return state.stringStartCol + 1;
13656 else
13657 return state.indented + indentUnit;
13658 }
13659 if (context && context.noIndent) return CodeMirror.Pass;
13660 if (state.tokenize != inTag && state.tokenize != inText)
13661 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13662 // Indent the starts of attribute names.
13663 if (state.tagName) {
13664 if (config.multilineTagIndentPastTag !== false)
13665 return state.tagStart + state.tagName.length + 2;
13666 else
13667 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13668 }
13669 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13670 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13671 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13672 while (context) {
13673 if (context.tagName == tagAfter[2]) {
13674 context = context.prev;
13675 break;
13676 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13677 context = context.prev;
13678 } else {
13679 break;
13680 }
13681 }
13682 } else if (tagAfter) { // Opening tag spotted
13683 while (context) {
13684 var grabbers = config.contextGrabbers[context.tagName];
13685 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13686 context = context.prev;
13687 else
13688 break;
13689 }
13690 }
13691 while (context && context.prev && !context.startOfLine)
13692 context = context.prev;
13693 if (context) return context.indent + indentUnit;
13694 else return state.baseIndent || 0;
13695 },
13696
13697 electricInput: /<\/[\s\w:]+>$/,
13698 blockCommentStart: "<!--",
13699 blockCommentEnd: "-->",
13700
13701 configuration: config.htmlMode ? "html" : "xml",
13702 helperType: config.htmlMode ? "html" : "xml",
13703
13704 skipAttribute: function(state) {
13705 if (state.state == attrValueState)
13706 state.state = attrState
13707 },
13708
13709 xmlCurrentTag: function(state) {
13710 return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
13711 },
13712
13713 xmlCurrentContext: function(state) {
13714 var context = []
13715 for (var cx = state.context; cx; cx = cx.prev)
13716 if (cx.tagName) context.push(cx.tagName)
13717 return context.reverse()
13718 }
13719 };
13720 });
13721
13722 CodeMirror.defineMIME("text/xml", "xml");
13723 CodeMirror.defineMIME("application/xml", "xml");
13724 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13725 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13726
13727 });
13728
13729 },{"../../lib/codemirror":14}],19:[function(require,module,exports){
13730 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;
13731
13732
13733 },{}],20:[function(require,module,exports){
13734 !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("preact")):"function"==typeof define&&define.amd?define(["preact"],t):e.preactRenderToString=t(e.preact)}(this,function(e){var t=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i,n=function(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},r=function(e,t){return String(e).replace(/(\n+)/g,"$1"+(t||"\t"))},o=function(e,t,n){return String(e).length>(t||40)||!n&&-1!==String(e).indexOf("\n")||-1!==String(e).indexOf("<")},i={};function a(e){var n="";for(var r in e){var o=e[r];null!=o&&(n&&(n+=" "),n+=i[r]||(i[r]=r.replace(/([A-Z])/g,"-$1").toLowerCase()),n+=": ",n+=o,"number"==typeof o&&!1===t.test(r)&&(n+="px"),n+=";")}return n||void 0}function l(e,t){for(var n in t)e[n]=t[n];return e}function s(e,t){return Array.isArray(t)?t.reduce(s,e):null!=t&&!1!==t&&e.push(t),e}var f={shallow:!0},c=[],p=/^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/,u=function(){};g.render=g;function g(t,i,f,d,_,h){if(null==t||"boolean"==typeof t)return"";Array.isArray(t)&&(t=e.createElement(e.Fragment,null,t));var v=t.type,m=t.props,y=!1;i=i||{};var x,b=(f=f||{}).pretty,S=b&&"string"==typeof b?b:"\t";if("object"!=typeof t&&!v)return n(t);if("function"==typeof v){if(y=!0,!f.shallow||!d&&!1!==f.renderRootComponent){if(v===e.Fragment){var w="",k=[];s(k,t.props.children);for(var O=0;O<k.length;O++)w+=(O>0&&b?"\n":"")+g(k[O],i,f,!1!==f.shallowHighOrder,_,h);return w}var C,A=t.__c={__v:t,context:i,props:t.props,setState:u,forceUpdate:u,__h:[]};if(e.options.__r&&e.options.__r(t),v.prototype&&"function"==typeof v.prototype.render){var F=v.contextType,j=F&&i[F.__c],H=null!=F?j?j.props.value:F.__:i;(A=t.__c=new v(m,H)).__v=t,A._dirty=A.__d=!0,A.props=m,null==A.state&&(A.state={}),null==A._nextState&&null==A.__s&&(A._nextState=A.__s=A.state),A.context=H,v.getDerivedStateFromProps?A.state=l(l({},A.state),v.getDerivedStateFromProps(A.props,A.state)):A.componentWillMount&&A.componentWillMount(),A.state=A._nextState!==A.state?A._nextState:A.__s!==A.state?A.__s:A.state,C=A.render(A.props,A.state,A.context)}else{var T=v.contextType,$=T&&i[T.__c];C=v.call(t.__c,m,null!=T?$?$.props.value:T.__:i)}return A.getChildContext&&(i=l(l({},i),A.getChildContext())),g(C,i,f,!1!==f.shallowHighOrder,_,h)}v=(x=v).displayName||x!==Function&&x.name||function(e){var t=(Function.prototype.toString.call(e).match(/^\s*function\s+([^( ]+)/)||"")[1];if(!t){for(var n=-1,r=c.length;r--;)if(c[r]===e){n=r;break}n<0&&(n=c.push(e)-1),t="UnnamedComponent"+n}return t}(x)}var L,M="";if(m){var R=Object.keys(m);f&&!0===f.sortAttributes&&R.sort();for(var q=0;q<R.length;q++){var D=R[q],E=m[D];if("children"!==D&&(!D.match(/[\s\n\\/='"\0<>]/)&&(f&&f.allAttributes||"key"!==D&&"ref"!==D))){if("className"===D){if(m.class)continue;D="class"}else _&&D.match(/^xlink:?./)&&(D=D.toLowerCase().replace(/^xlink:?/,"xlink:"));if("htmlFor"===D){if(m.for)continue;D="for"}"style"===D&&E&&"object"==typeof E&&(E=a(E));var N=f.attributeHook&&f.attributeHook(D,E,i,f,y);if(N||""===N)M+=N;else if("dangerouslySetInnerHTML"===D)L=E&&E.__html;else if((E||0===E||""===E)&&"function"!=typeof E){if(!(!0!==E&&""!==E||(E=D,f&&f.xml))){M+=" "+D;continue}if("value"===D){if("select"===v){h=E;continue}"option"===v&&h==E&&(M+=" selected")}M+=" "+D+'="'+n(E)+'"'}}}}if(b){var P=M.replace(/^\n\s*/," ");P===M||~P.indexOf("\n")?b&&~M.indexOf("\n")&&(M+="\n"):M=P}if(M="<"+v+M+">",String(v).match(/[\s\n\\/='"\0<>]/))throw new Error(v+" is not a valid HTML tag name in "+M);var U=String(v).match(p);U&&(M=M.replace(/>$/," />"));var W,z=[];if(L)b&&o(L)&&(L="\n"+S+r(L,S)),M+=L;else if(m&&s(W=[],m.children).length){for(var I=b&&~M.indexOf("\n"),Z=!1,B=0;B<W.length;B++){var G=W[B];if(null!=G&&!1!==G){var J=g(G,i,f,!0,"svg"===v||"foreignObject"!==v&&_,h);if(b&&!I&&o(J)&&(I=!0),J)if(b){var K=J.length>0&&"<"!=J[0];Z&&K?z[z.length-1]+=J:z.push(J),Z=K}else z.push(J)}}if(b&&I)for(var Q=z.length;Q--;)z[Q]="\n"+S+r(z[Q],S)}if(z.length)M+=z.join("");else if(f&&f.xml)return M.substring(0,M.length-1)+" />";return U||(b&&~M.indexOf("\n")&&(M+="\n"),M+="</"+v+">"),M}return g.shallowRender=function(e,t){return g(e,t,f)},g});
13735
13736
13737 },{"preact":21}],21:[function(require,module,exports){
13738 var n,l,u,t,i,r,o,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function p(n){var l=n.parentNode;l&&l.removeChild(n)}function v(n,l,u){var t,i=arguments,r={};for(t in l)"key"!==t&&"ref"!==t&&(r[t]=l[t]);if(arguments.length>3)for(u=[u],t=3;t<arguments.length;t++)u.push(i[t]);if(null!=u&&(r.children=u),"function"==typeof n&&null!=n.defaultProps)for(t in n.defaultProps)void 0===r[t]&&(r[t]=n.defaultProps[t]);return h(n,r,l&&l.key,l&&l.ref,null)}function h(l,u,t,i,r){var o={type:l,props:u,key:t,ref:i,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:r};return null==r&&(o.__v=o),n.vnode&&n.vnode(o),o}function y(n){return n.children}function d(n,l){this.props=n,this.context=l}function x(n,l){if(null==l)return n.__?x(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return"function"==typeof n.type?x(n):null}function m(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return m(n)}}function w(l){(!l.__d&&(l.__d=!0)&&u.push(l)&&!t++||r!==n.debounceRendering)&&((r=n.debounceRendering)||i)(k)}function k(){for(var n;t=u.length;)n=u.sort(function(n,l){return n.__v.__b-l.__v.__b}),u=[],n.some(function(n){var l,u,t,i,r,o,f;n.__d&&(o=(r=(l=n).__v).__e,(f=l.__P)&&(u=[],(t=a({},r)).__v=t,i=z(f,r,t,l.__n,void 0!==f.ownerSVGElement,null,u,null==o?x(r):o),A(u,r),i!=o&&m(r)))})}function g(n,l,u,t,i,r,o,f,s){var a,v,h,y,d,m,w,k=u&&u.__k||c,g=k.length;if(f==e&&(f=null!=r?r[0]:g?x(u,0):null),a=0,l.__k=_(l.__k,function(u){if(null!=u){if(u.__=l,u.__b=l.__b+1,null===(h=k[a])||h&&u.key==h.key&&u.type===h.type)k[a]=void 0;else for(v=0;v<g;v++){if((h=k[v])&&u.key==h.key&&u.type===h.type){k[v]=void 0;break}h=null}if(y=z(n,u,h=h||e,t,i,r,o,f,s),(v=u.ref)&&h.ref!=v&&(w||(w=[]),h.ref&&w.push(h.ref,null,u),w.push(v,u.__c||y,u)),null!=y){var c;if(null==m&&(m=y),void 0!==u.__d)c=u.__d,u.__d=void 0;else if(r==h||y!=f||null==y.parentNode){n:if(null==f||f.parentNode!==n)n.appendChild(y),c=null;else{for(d=f,v=0;(d=d.nextSibling)&&v<g;v+=2)if(d==y)break n;n.insertBefore(y,f),c=f}"option"==l.type&&(n.value="")}f=void 0!==c?c:y.nextSibling,"function"==typeof l.type&&(l.__d=f)}else f&&h.__e==f&&f.parentNode!=n&&(f=x(h))}return a++,u}),l.__e=m,null!=r&&"function"!=typeof l.type)for(a=r.length;a--;)null!=r[a]&&p(r[a]);for(a=g;a--;)null!=k[a]&&j(k[a],k[a]);if(w)for(a=0;a<w.length;a++)$(w[a],w[++a],w[++a])}function _(n,l,u){if(null==u&&(u=[]),null==n||"boolean"==typeof n)l&&u.push(l(null));else if(Array.isArray(n))for(var t=0;t<n.length;t++)_(n[t],l,u);else u.push(l?l("string"==typeof n||"number"==typeof n?h(null,n,null,null,n):null!=n.__e||null!=n.__c?h(n.type,n.props,n.key,null,n.__v):n):n);return u}function b(n,l,u,t,i){var r;for(r in u)"children"===r||"key"===r||r in l||C(n,r,null,u[r],t);for(r in l)i&&"function"!=typeof l[r]||"children"===r||"key"===r||"value"===r||"checked"===r||u[r]===l[r]||C(n,r,l[r],u[r],t)}function P(n,l,u){"-"===l[0]?n.setProperty(l,u):n[l]="number"==typeof u&&!1===s.test(l)?u+"px":null==u?"":u}function C(n,l,u,t,i){var r,o,f,e,c;if(i?"className"===l&&(l="class"):"class"===l&&(l="className"),"style"===l)if(r=n.style,"string"==typeof u)r.cssText=u;else{if("string"==typeof t&&(r.cssText="",t=null),t)for(e in t)u&&e in u||P(r,e,"");if(u)for(c in u)t&&u[c]===t[c]||P(r,c,u[c])}else"o"===l[0]&&"n"===l[1]?(o=l!==(l=l.replace(/Capture$/,"")),f=l.toLowerCase(),l=(f in n?f:l).slice(2),u?(t||n.addEventListener(l,N,o),(n.l||(n.l={}))[l]=u):n.removeEventListener(l,N,o)):"list"!==l&&"tagName"!==l&&"form"!==l&&"type"!==l&&"size"!==l&&!i&&l in n?n[l]=null==u?"":u:"function"!=typeof u&&"dangerouslySetInnerHTML"!==l&&(l!==(l=l.replace(/^xlink:?/,""))?null==u||!1===u?n.removeAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase()):n.setAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase(),u):null==u||!1===u&&!/^ar/.test(l)?n.removeAttribute(l):n.setAttribute(l,u))}function N(l){this.l[l.type](n.event?n.event(l):l)}function z(l,u,t,i,r,o,f,e,c){var s,p,v,h,x,m,w,k,_,b,P=u.type;if(void 0!==u.constructor)return null;(s=n.__b)&&s(u);try{n:if("function"==typeof P){if(k=u.props,_=(s=P.contextType)&&i[s.__c],b=s?_?_.props.value:s.__:i,t.__c?w=(p=u.__c=t.__c).__=p.__E:("prototype"in P&&P.prototype.render?u.__c=p=new P(k,b):(u.__c=p=new d(k,b),p.constructor=P,p.render=D),_&&_.sub(p),p.props=k,p.state||(p.state={}),p.context=b,p.__n=i,v=p.__d=!0,p.__h=[]),null==p.__s&&(p.__s=p.state),null!=P.getDerivedStateFromProps&&(p.__s==p.state&&(p.__s=a({},p.__s)),a(p.__s,P.getDerivedStateFromProps(k,p.__s))),h=p.props,x=p.state,v)null==P.getDerivedStateFromProps&&null!=p.componentWillMount&&p.componentWillMount(),null!=p.componentDidMount&&p.__h.push(p.componentDidMount);else{if(null==P.getDerivedStateFromProps&&k!==h&&null!=p.componentWillReceiveProps&&p.componentWillReceiveProps(k,b),!p.__e&&null!=p.shouldComponentUpdate&&!1===p.shouldComponentUpdate(k,p.__s,b)||u.__v===t.__v&&!p.__){for(p.props=k,p.state=p.__s,u.__v!==t.__v&&(p.__d=!1),p.__v=u,u.__e=t.__e,u.__k=t.__k,p.__h.length&&f.push(p),s=0;s<u.__k.length;s++)u.__k[s]&&(u.__k[s].__=u);break n}null!=p.componentWillUpdate&&p.componentWillUpdate(k,p.__s,b),null!=p.componentDidUpdate&&p.__h.push(function(){p.componentDidUpdate(h,x,m)})}p.context=b,p.props=k,p.state=p.__s,(s=n.__r)&&s(u),p.__d=!1,p.__v=u,p.__P=l,s=p.render(p.props,p.state,p.context),u.__k=null!=s&&s.type==y&&null==s.key?s.props.children:Array.isArray(s)?s:[s],null!=p.getChildContext&&(i=a(a({},i),p.getChildContext())),v||null==p.getSnapshotBeforeUpdate||(m=p.getSnapshotBeforeUpdate(h,x)),g(l,u,t,i,r,o,f,e,c),p.base=u.__e,p.__h.length&&f.push(p),w&&(p.__E=p.__=null),p.__e=!1}else null==o&&u.__v===t.__v?(u.__k=t.__k,u.__e=t.__e):u.__e=T(t.__e,u,t,i,r,o,f,c);(s=n.diffed)&&s(u)}catch(l){u.__v=null,n.__e(l,u,t)}return u.__e}function A(l,u){n.__c&&n.__c(u,l),l.some(function(u){try{l=u.__h,u.__h=[],l.some(function(n){n.call(u)})}catch(l){n.__e(l,u.__v)}})}function T(n,l,u,t,i,r,o,f){var s,a,p,v,h,y=u.props,d=l.props;if(i="svg"===l.type||i,null!=r)for(s=0;s<r.length;s++)if(null!=(a=r[s])&&((null===l.type?3===a.nodeType:a.localName===l.type)||n==a)){n=a,r[s]=null;break}if(null==n){if(null===l.type)return document.createTextNode(d);n=i?document.createElementNS("http://www.w3.org/2000/svg",l.type):document.createElement(l.type,d.is&&{is:d.is}),r=null,f=!1}if(null===l.type)y!==d&&n.data!=d&&(n.data=d);else{if(null!=r&&(r=c.slice.call(n.childNodes)),p=(y=u.props||e).dangerouslySetInnerHTML,v=d.dangerouslySetInnerHTML,!f){if(y===e)for(y={},h=0;h<n.attributes.length;h++)y[n.attributes[h].name]=n.attributes[h].value;(v||p)&&(v&&p&&v.__html==p.__html||(n.innerHTML=v&&v.__html||""))}b(n,d,y,i,f),v?l.__k=[]:(l.__k=l.props.children,g(n,l,u,t,"foreignObject"!==l.type&&i,r,o,e,f)),f||("value"in d&&void 0!==(s=d.value)&&s!==n.value&&C(n,"value",s,y.value,!1),"checked"in d&&void 0!==(s=d.checked)&&s!==n.checked&&C(n,"checked",s,y.checked,!1))}return n}function $(l,u,t){try{"function"==typeof l?l(u):l.current=u}catch(l){n.__e(l,t)}}function j(l,u,t){var i,r,o;if(n.unmount&&n.unmount(l),(i=l.ref)&&(i.current&&i.current!==l.__e||$(i,null,u)),t||"function"==typeof l.type||(t=null!=(r=l.__e)),l.__e=l.__d=void 0,null!=(i=l.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(l){n.__e(l,u)}i.base=i.__P=null}if(i=l.__k)for(o=0;o<i.length;o++)i[o]&&j(i[o],u,t);null!=r&&p(r)}function D(n,l,u){return this.constructor(n,u)}function E(l,u,t){var i,r,f;n.__&&n.__(l,u),r=(i=t===o)?null:t&&t.__k||u.__k,l=v(y,null,[l]),f=[],z(u,(i?u:t||u).__k=l,r||e,e,void 0!==u.ownerSVGElement,t&&!i?[t]:r?null:c.slice.call(u.childNodes),f,t||e,i),A(f,l)}n={__e:function(n,l){for(var u,t;l=l.__;)if((u=l.__c)&&!u.__)try{if(u.constructor&&null!=u.constructor.getDerivedStateFromError&&(t=!0,u.setState(u.constructor.getDerivedStateFromError(n))),null!=u.componentDidCatch&&(t=!0,u.componentDidCatch(n)),t)return w(u.__E=u)}catch(l){n=l}throw n}},l=function(n){return null!=n&&void 0===n.constructor},d.prototype.setState=function(n,l){var u;u=this.__s!==this.state?this.__s:this.__s=a({},this.state),"function"==typeof n&&(n=n(u,this.props)),n&&a(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),w(this))},d.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),w(this))},d.prototype.render=y,u=[],t=0,i="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,o=e,f=0,exports.render=E,exports.hydrate=function(n,l){E(n,l,o)},exports.createElement=v,exports.h=v,exports.Fragment=y,exports.createRef=function(){return{}},exports.isValidElement=l,exports.Component=d,exports.cloneElement=function(n,l){var u,t;for(t in l=a(a({},n.props),l),arguments.length>2&&(l.children=c.slice.call(arguments,2)),u={},l)"key"!==t&&"ref"!==t&&(u[t]=l[t]);return h(n.type,u,l.key||n.key,l.ref||n.ref,null)},exports.createContext=function(n){var l={},u={__c:"__cC"+f++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var t,i=this;return this.getChildContext||(t=[],this.getChildContext=function(){return l[u.__c]=i,l},this.shouldComponentUpdate=function(n){i.props.value!==n.value&&t.some(function(l){l.context=n.value,w(l)})},this.sub=function(n){t.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){t.splice(t.indexOf(n),1),l&&l.call(n)}}),n.children}};return u.Consumer.contextType=u,u.Provider.__=u,u},exports.toChildArray=_,exports._e=j,exports.options=n;
13739
13740
13741 },{}],22:[function(require,module,exports){
13742 function tlite(getTooltipOpts) {
13743 document.addEventListener('mouseover', function (e) {
13744 var el = e.target;
13745 var opts = getTooltipOpts(el);
13746
13747 if (!opts) {
13748 el = el.parentElement;
13749 opts = el && getTooltipOpts(el);
13750 }
13751
13752 opts && tlite.show(el, opts, true);
13753 });
13754 }
13755
13756 tlite.show = function (el, opts, isAuto) {
13757 var fallbackAttrib = 'data-tlite';
13758 opts = opts || {};
13759
13760 (el.tooltip || Tooltip(el, opts)).show();
13761
13762 function Tooltip(el, opts) {
13763 var tooltipEl;
13764 var showTimer;
13765 var text;
13766
13767 el.addEventListener('mousedown', autoHide);
13768 el.addEventListener('mouseleave', autoHide);
13769
13770 function show() {
13771 text = el.title || el.getAttribute(fallbackAttrib) || text;
13772 el.title = '';
13773 el.setAttribute(fallbackAttrib, '');
13774 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
13775 }
13776
13777 function autoHide() {
13778 tlite.hide(el, true);
13779 }
13780
13781 function hide(isAutoHiding) {
13782 if (isAuto === isAutoHiding) {
13783 showTimer = clearTimeout(showTimer);
13784 var parent = tooltipEl && tooltipEl.parentNode;
13785 parent && parent.removeChild(tooltipEl);
13786 tooltipEl = undefined;
13787 }
13788 }
13789
13790 function fadeIn() {
13791 if (!tooltipEl) {
13792 tooltipEl = createTooltip(el, text, opts);
13793 }
13794 }
13795
13796 return el.tooltip = {
13797 show: show,
13798 hide: hide
13799 };
13800 }
13801
13802 function createTooltip(el, text, opts) {
13803 var tooltipEl = document.createElement('span');
13804 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
13805
13806 tooltipEl.innerHTML = text;
13807
13808 el.appendChild(tooltipEl);
13809
13810 var vertGrav = grav[0] || '';
13811 var horzGrav = grav[1] || '';
13812
13813 function positionTooltip() {
13814 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
13815
13816 var arrowSize = 10;
13817 var top = el.offsetTop;
13818 var left = el.offsetLeft;
13819
13820 if (tooltipEl.offsetParent === el) {
13821 top = left = 0;
13822 }
13823
13824 var width = el.offsetWidth;
13825 var height = el.offsetHeight;
13826 var tooltipHeight = tooltipEl.offsetHeight;
13827 var tooltipWidth = tooltipEl.offsetWidth;
13828 var centerEl = left + (width / 2);
13829
13830 tooltipEl.style.top = (
13831 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
13832 vertGrav === 'n' ? (top + height + arrowSize) :
13833 (top + (height / 2) - (tooltipHeight / 2))
13834 ) + 'px';
13835
13836 tooltipEl.style.left = (
13837 horzGrav === 'w' ? left :
13838 horzGrav === 'e' ? left + width - tooltipWidth :
13839 vertGrav === 'w' ? (left + width + arrowSize) :
13840 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
13841 (centerEl - tooltipWidth / 2)
13842 ) + 'px';
13843 }
13844
13845 positionTooltip();
13846
13847 var rect = tooltipEl.getBoundingClientRect();
13848
13849 if (vertGrav === 's' && rect.top < 0) {
13850 vertGrav = 'n';
13851 positionTooltip();
13852 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
13853 vertGrav = 's';
13854 positionTooltip();
13855 } else if (vertGrav === 'e' && rect.left < 0) {
13856 vertGrav = 'w';
13857 positionTooltip();
13858 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
13859 vertGrav = 'e';
13860 positionTooltip();
13861 }
13862
13863 tooltipEl.className += ' tlite-visible';
13864
13865 return tooltipEl;
13866 }
13867 };
13868
13869 tlite.hide = function (el, isAuto) {
13870 el.tooltip && el.tooltip.hide(isAuto);
13871 };
13872
13873 if (typeof module !== 'undefined' && module.exports) {
13874 module.exports = tlite;
13875 }
13876
13877 },{}]},{},[2]);
13878 ; })();