1// CodeMirror is the only global var we claim
2window.CodeMirror = (function() {
3  "use strict";
4
5  // BROWSER SNIFFING
6
7  // Crude, but necessary to handle a number of hard-to-feature-detect
8  // bugs and behavior differences.
9  var gecko = /gecko\/\d/i.test(navigator.userAgent);
10  var ie = /MSIE \d/.test(navigator.userAgent);
11  var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
12  var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
13  var webkit = /WebKit\//.test(navigator.userAgent);
14  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
15  var chrome = /Chrome\//.test(navigator.userAgent);
16  var opera = /Opera\//.test(navigator.userAgent);
17  var safari = /Apple Computer/.test(navigator.vendor);
18  var khtml = /KHTML\//.test(navigator.userAgent);
19  var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
20  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
21  var phantom = /PhantomJS/.test(navigator.userAgent);
22
23  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
24  // This is woefully incomplete. Suggestions for alternative methods welcome.
25  var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
26  var mac = ios || /Mac/.test(navigator.platform);
27  var windows = /windows/i.test(navigator.platform);
28
29  var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
30  if (opera_version) opera_version = Number(opera_version[1]);
31  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
32  var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
33  var captureMiddleClick = gecko || (ie && !ie_lt9);
34
35  // Optimize some code when these features are not used
36  var sawReadOnlySpans = false, sawCollapsedSpans = false;
37
38  // CONSTRUCTOR
39
40  function CodeMirror(place, options) {
41    if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
42
43    this.options = options = options || {};
44    // Determine effective options based on given values and defaults.
45    for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
46      options[opt] = defaults[opt];
47    setGuttersForLineNumbers(options);
48
49    var docStart = typeof options.value == "string" ? 0 : options.value.first;
50    var display = this.display = makeDisplay(place, docStart);
51    display.wrapper.CodeMirror = this;
52    updateGutters(this);
53    if (options.autofocus && !mobile) focusInput(this);
54
55    this.state = {keyMaps: [],
56                  overlays: [],
57                  modeGen: 0,
58                  overwrite: false, focused: false,
59                  suppressEdits: false, pasteIncoming: false,
60                  draggingText: false,
61                  highlight: new Delayed()};
62
63    themeChanged(this);
64    if (options.lineWrapping)
65      this.display.wrapper.className += " CodeMirror-wrap";
66
67    var doc = options.value;
68    if (typeof doc == "string") doc = new Doc(options.value, options.mode);
69    operation(this, attachDoc)(this, doc);
70
71    // Override magic textarea content restore that IE sometimes does
72    // on our hidden textarea on reload
73    if (ie) setTimeout(bind(resetInput, this, true), 20);
74
75    registerEventHandlers(this);
76    // IE throws unspecified error in certain cases, when
77    // trying to access activeElement before onload
78    var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
79    if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
80    else onBlur(this);
81
82    operation(this, function() {
83      for (var opt in optionHandlers)
84        if (optionHandlers.propertyIsEnumerable(opt))
85          optionHandlers[opt](this, options[opt], Init);
86      for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
87    })();
88  }
89
90  // DISPLAY CONSTRUCTOR
91
92  function makeDisplay(place, docStart) {
93    var d = {};
94
95    var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
96    if (webkit) input.style.width = "1000px";
97    else input.setAttribute("wrap", "off");
98    // if border: 0; -- iOS fails to open keyboard (issue #1287)
99    if (ios) input.style.border = "1px solid black";
100    input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
101
102    // Wraps and hides input textarea
103    d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
104    // The actual fake scrollbars.
105    d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
106    d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
107    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
108    // DIVs containing the selection and the actual code
109    d.lineDiv = elt("div");
110    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
111    // Blinky cursor, and element used to ensure cursor fits at the end of a line
112    d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
113    // Secondary cursor, shown when on a 'jump' in bi-directional text
114    d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
115    // Used to measure text size
116    d.measure = elt("div", null, "CodeMirror-measure");
117    // Wraps everything that needs to exist inside the vertically-padded coordinate system
118    d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
119                         null, "position: relative; outline: none");
120    // Moved around its parent to cover visible view
121    d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
122    // Set to the height of the text, causes scrolling
123    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
124    // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
125    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
126    // Will contain the gutters, if any
127    d.gutters = elt("div", null, "CodeMirror-gutters");
128    d.lineGutter = null;
129    // Helper element to properly size the gutter backgrounds
130    var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
131    // Provides scrolling
132    d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
133    d.scroller.setAttribute("tabIndex", "-1");
134    // The element in which the editor lives.
135    d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
136                            d.scrollbarFiller, d.scroller], "CodeMirror");
137    // Work around IE7 z-index bug
138    if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
139    if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
140
141    // Needed to hide big blue blinking cursor on Mobile Safari
142    if (ios) input.style.width = "0px";
143    if (!webkit) d.scroller.draggable = true;
144    // Needed to handle Tab key in KHTML
145    if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
146    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
147    else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
148
149    // Current visible range (may be bigger than the view window).
150    d.viewOffset = d.lastSizeC = 0;
151    d.showingFrom = d.showingTo = docStart;
152
153    // Used to only resize the line number gutter when necessary (when
154    // the amount of lines crosses a boundary that makes its width change)
155    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
156    // See readInput and resetInput
157    d.prevInput = "";
158    // Set to true when a non-horizontal-scrolling widget is added. As
159    // an optimization, widget aligning is skipped when d is false.
160    d.alignWidgets = false;
161    // Flag that indicates whether we currently expect input to appear
162    // (after some event like 'keypress' or 'input') and are polling
163    // intensively.
164    d.pollingFast = false;
165    // Self-resetting timeout for the poller
166    d.poll = new Delayed();
167    // True when a drag from the editor is active
168    d.draggingText = false;
169
170    d.cachedCharWidth = d.cachedTextHeight = null;
171    d.measureLineCache = [];
172    d.measureLineCachePos = 0;
173
174    // Tracks when resetInput has punted to just putting a short
175    // string instead of the (large) selection.
176    d.inaccurateSelection = false;
177
178    // Tracks the maximum line length so that the horizontal scrollbar
179    // can be kept static when scrolling.
180    d.maxLine = null;
181    d.maxLineLength = 0;
182    d.maxLineChanged = false;
183
184    // Used for measuring wheel scrolling granularity
185    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
186
187    return d;
188  }
189
190  // STATE UPDATES
191
192  // Used to get the editor into a consistent state again when options change.
193
194  function loadMode(cm) {
195    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
196    cm.doc.iter(function(line) {
197      if (line.stateAfter) line.stateAfter = null;
198      if (line.styles) line.styles = null;
199    });
200    cm.doc.frontier = cm.doc.first;
201    startWorker(cm, 100);
202    cm.state.modeGen++;
203    if (cm.curOp) regChange(cm);
204  }
205
206  function wrappingChanged(cm) {
207    if (cm.options.lineWrapping) {
208      cm.display.wrapper.className += " CodeMirror-wrap";
209      cm.display.sizer.style.minWidth = "";
210    } else {
211      cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
212      computeMaxLength(cm);
213    }
214    estimateLineHeights(cm);
215    regChange(cm);
216    clearCaches(cm);
217    setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
218  }
219
220  function estimateHeight(cm) {
221    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
222    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
223    return function(line) {
224      if (lineIsHidden(cm.doc, line))
225        return 0;
226      else if (wrapping)
227        return (Math.ceil(line.text.length / perLine) || 1) * th;
228      else
229        return th;
230    };
231  }
232
233  function estimateLineHeights(cm) {
234    var doc = cm.doc, est = estimateHeight(cm);
235    doc.iter(function(line) {
236      var estHeight = est(line);
237      if (estHeight != line.height) updateLineHeight(line, estHeight);
238    });
239  }
240
241  function keyMapChanged(cm) {
242    var style = keyMap[cm.options.keyMap].style;
243    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
244      (style ? " cm-keymap-" + style : "");
245  }
246
247  function themeChanged(cm) {
248    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
249      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
250    clearCaches(cm);
251  }
252
253  function guttersChanged(cm) {
254    updateGutters(cm);
255    regChange(cm);
256  }
257
258  function updateGutters(cm) {
259    var gutters = cm.display.gutters, specs = cm.options.gutters;
260    removeChildren(gutters);
261    for (var i = 0; i < specs.length; ++i) {
262      var gutterClass = specs[i];
263      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
264      if (gutterClass == "CodeMirror-linenumbers") {
265        cm.display.lineGutter = gElt;
266        gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
267      }
268    }
269    gutters.style.display = i ? "" : "none";
270  }
271
272  function lineLength(doc, line) {
273    if (line.height == 0) return 0;
274    var len = line.text.length, merged, cur = line;
275    while (merged = collapsedSpanAtStart(cur)) {
276      var found = merged.find();
277      cur = getLine(doc, found.from.line);
278      len += found.from.ch - found.to.ch;
279    }
280    cur = line;
281    while (merged = collapsedSpanAtEnd(cur)) {
282      var found = merged.find();
283      len -= cur.text.length - found.from.ch;
284      cur = getLine(doc, found.to.line);
285      len += cur.text.length - found.to.ch;
286    }
287    return len;
288  }
289
290  function computeMaxLength(cm) {
291    var d = cm.display, doc = cm.doc;
292    d.maxLine = getLine(doc, doc.first);
293    d.maxLineLength = lineLength(doc, d.maxLine);
294    d.maxLineChanged = true;
295    doc.iter(function(line) {
296      var len = lineLength(doc, line);
297      if (len > d.maxLineLength) {
298        d.maxLineLength = len;
299        d.maxLine = line;
300      }
301    });
302  }
303
304  // Make sure the gutters options contains the element
305  // "CodeMirror-linenumbers" when the lineNumbers option is true.
306  function setGuttersForLineNumbers(options) {
307    var found = false;
308    for (var i = 0; i < options.gutters.length; ++i) {
309      if (options.gutters[i] == "CodeMirror-linenumbers") {
310        if (options.lineNumbers) found = true;
311        else options.gutters.splice(i--, 1);
312      }
313    }
314    if (!found && options.lineNumbers)
315      options.gutters.push("CodeMirror-linenumbers");
316  }
317
318  // SCROLLBARS
319
320  // Re-synchronize the fake scrollbars with the actual size of the
321  // content. Optionally force a scrollTop.
322  function updateScrollbars(d /* display */, docHeight) {
323    var totalHeight = docHeight + paddingVert(d);
324    d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
325    var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
326    var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
327    var needsV = scrollHeight > d.scroller.clientHeight;
328    if (needsV) {
329      d.scrollbarV.style.display = "block";
330      d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
331      d.scrollbarV.firstChild.style.height =
332        (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
333    } else d.scrollbarV.style.display = "";
334    if (needsH) {
335      d.scrollbarH.style.display = "block";
336      d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
337      d.scrollbarH.firstChild.style.width =
338        (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
339    } else d.scrollbarH.style.display = "";
340    if (needsH && needsV) {
341      d.scrollbarFiller.style.display = "block";
342      d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
343    } else d.scrollbarFiller.style.display = "";
344
345    if (mac_geLion && scrollbarWidth(d.measure) === 0)
346      d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
347  }
348
349  function visibleLines(display, doc, viewPort) {
350    var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
351    if (typeof viewPort == "number") top = viewPort;
352    else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
353    top = Math.floor(top - paddingTop(display));
354    var bottom = Math.ceil(top + height);
355    return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
356  }
357
358  // LINE NUMBERS
359
360  function alignHorizontally(cm) {
361    var display = cm.display;
362    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
363    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
364    var gutterW = display.gutters.offsetWidth, l = comp + "px";
365    for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
366      for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
367    }
368    if (cm.options.fixedGutter)
369      display.gutters.style.left = (comp + gutterW) + "px";
370  }
371
372  function maybeUpdateLineNumberWidth(cm) {
373    if (!cm.options.lineNumbers) return false;
374    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
375    if (last.length != display.lineNumChars) {
376      var test = display.measure.appendChild(elt("div", [elt("div", last)],
377                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
378      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
379      display.lineGutter.style.width = "";
380      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
381      display.lineNumWidth = display.lineNumInnerWidth + padding;
382      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
383      display.lineGutter.style.width = display.lineNumWidth + "px";
384      return true;
385    }
386    return false;
387  }
388
389  function lineNumberFor(options, i) {
390    return String(options.lineNumberFormatter(i + options.firstLineNumber));
391  }
392  function compensateForHScroll(display) {
393    return getRect(display.scroller).left - getRect(display.sizer).left;
394  }
395
396  // DISPLAY DRAWING
397
398  function updateDisplay(cm, changes, viewPort) {
399    var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
400    var updated = updateDisplayInner(cm, changes, viewPort);
401    if (updated) {
402      signalLater(cm, "update", cm);
403      if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
404        signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
405    }
406    updateSelection(cm);
407    updateScrollbars(cm.display, cm.doc.height);
408
409    return updated;
410  }
411
412  // Uses a set of changes plus the current scroll position to
413  // determine which DOM updates have to be made, and makes the
414  // updates.
415  function updateDisplayInner(cm, changes, viewPort) {
416    var display = cm.display, doc = cm.doc;
417    if (!display.wrapper.clientWidth) {
418      display.showingFrom = display.showingTo = doc.first;
419      display.viewOffset = 0;
420      return;
421    }
422
423    // Compute the new visible window
424    // If scrollTop is specified, use that to determine which lines
425    // to render instead of the current scrollbar position.
426    var visible = visibleLines(display, doc, viewPort);
427    // Bail out if the visible area is already rendered and nothing changed.
428    if (changes.length == 0 &&
429        visible.from > display.showingFrom && visible.to < display.showingTo)
430      return;
431
432    if (maybeUpdateLineNumberWidth(cm))
433      changes = [{from: doc.first, to: doc.first + doc.size}];
434    var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
435    display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
436
437    // Used to determine which lines need their line numbers updated
438    var positionsChangedFrom = Infinity;
439    if (cm.options.lineNumbers)
440      for (var i = 0; i < changes.length; ++i)
441        if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
442
443    var end = doc.first + doc.size;
444    var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
445    var to = Math.min(end, visible.to + cm.options.viewportMargin);
446    if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
447    if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
448    if (sawCollapsedSpans) {
449      from = lineNo(visualLine(doc, getLine(doc, from)));
450      while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
451    }
452
453    // Create a range of theoretically intact lines, and punch holes
454    // in that using the change info.
455    var intact = [{from: Math.max(display.showingFrom, doc.first),
456                   to: Math.min(display.showingTo, end)}];
457    if (intact[0].from >= intact[0].to) intact = [];
458    else intact = computeIntact(intact, changes);
459    // When merged lines are present, we might have to reduce the
460    // intact ranges because changes in continued fragments of the
461    // intact lines do require the lines to be redrawn.
462    if (sawCollapsedSpans)
463      for (var i = 0; i < intact.length; ++i) {
464        var range = intact[i], merged;
465        while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
466          var newTo = merged.find().from.line;
467          if (newTo > range.from) range.to = newTo;
468          else { intact.splice(i--, 1); break; }
469        }
470      }
471
472    // Clip off the parts that won't be visible
473    var intactLines = 0;
474    for (var i = 0; i < intact.length; ++i) {
475      var range = intact[i];
476      if (range.from < from) range.from = from;
477      if (range.to > to) range.to = to;
478      if (range.from >= range.to) intact.splice(i--, 1);
479      else intactLines += range.to - range.from;
480    }
481    if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
482      updateViewOffset(cm);
483      return;
484    }
485    intact.sort(function(a, b) {return a.from - b.from;});
486
487    var focused = document.activeElement;
488    if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
489    patchDisplay(cm, from, to, intact, positionsChangedFrom);
490    display.lineDiv.style.display = "";
491    if (document.activeElement != focused && focused.offsetHeight) focused.focus();
492
493    var different = from != display.showingFrom || to != display.showingTo ||
494      display.lastSizeC != display.wrapper.clientHeight;
495    // This is just a bogus formula that detects when the editor is
496    // resized or the font size changes.
497    if (different) display.lastSizeC = display.wrapper.clientHeight;
498    display.showingFrom = from; display.showingTo = to;
499    startWorker(cm, 100);
500
501    var prevBottom = display.lineDiv.offsetTop;
502    for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
503      if (ie_lt8) {
504        var bot = node.offsetTop + node.offsetHeight;
505        height = bot - prevBottom;
506        prevBottom = bot;
507      } else {
508        var box = getRect(node);
509        height = box.bottom - box.top;
510      }
511      var diff = node.lineObj.height - height;
512      if (height < 2) height = textHeight(display);
513      if (diff > .001 || diff < -.001) {
514        updateLineHeight(node.lineObj, height);
515        var widgets = node.lineObj.widgets;
516        if (widgets) for (var i = 0; i < widgets.length; ++i)
517          widgets[i].height = widgets[i].node.offsetHeight;
518      }
519    }
520    updateViewOffset(cm);
521
522    if (visibleLines(display, doc, viewPort).to > to)
523      updateDisplayInner(cm, [], viewPort);
524    return true;
525  }
526
527  function updateViewOffset(cm) {
528    var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
529    // Position the mover div to align with the current virtual scroll position
530    cm.display.mover.style.top = off + "px";
531  }
532
533  function computeIntact(intact, changes) {
534    for (var i = 0, l = changes.length || 0; i < l; ++i) {
535      var change = changes[i], intact2 = [], diff = change.diff || 0;
536      for (var j = 0, l2 = intact.length; j < l2; ++j) {
537        var range = intact[j];
538        if (change.to <= range.from && change.diff) {
539          intact2.push({from: range.from + diff, to: range.to + diff});
540        } else if (change.to <= range.from || change.from >= range.to) {
541          intact2.push(range);
542        } else {
543          if (change.from > range.from)
544            intact2.push({from: range.from, to: change.from});
545          if (change.to < range.to)
546            intact2.push({from: change.to + diff, to: range.to + diff});
547        }
548      }
549      intact = intact2;
550    }
551    return intact;
552  }
553
554  function getDimensions(cm) {
555    var d = cm.display, left = {}, width = {};
556    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
557      left[cm.options.gutters[i]] = n.offsetLeft;
558      width[cm.options.gutters[i]] = n.offsetWidth;
559    }
560    return {fixedPos: compensateForHScroll(d),
561            gutterTotalWidth: d.gutters.offsetWidth,
562            gutterLeft: left,
563            gutterWidth: width,
564            wrapperWidth: d.wrapper.clientWidth};
565  }
566
567  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
568    var dims = getDimensions(cm);
569    var display = cm.display, lineNumbers = cm.options.lineNumbers;
570    if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
571      removeChildren(display.lineDiv);
572    var container = display.lineDiv, cur = container.firstChild;
573
574    function rm(node) {
575      var next = node.nextSibling;
576      if (webkit && mac && cm.display.currentWheelTarget == node) {
577        node.style.display = "none";
578        node.lineObj = null;
579      } else {
580        node.parentNode.removeChild(node);
581      }
582      return next;
583    }
584
585    var nextIntact = intact.shift(), lineN = from;
586    cm.doc.iter(from, to, function(line) {
587      if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
588      if (lineIsHidden(cm.doc, line)) {
589        if (line.height != 0) updateLineHeight(line, 0);
590        if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
591          if (line.widgets[i].showIfHidden) {
592            var prev = cur.previousSibling;
593            if (/pre/i.test(prev.nodeName)) {
594              var wrap = elt("div", null, null, "position: relative");
595              prev.parentNode.replaceChild(wrap, prev);
596              wrap.appendChild(prev);
597              prev = wrap;
598            }
599            var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
600            positionLineWidget(line.widgets[i], wnode, prev, dims);
601          }
602      } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
603        // This line is intact. Skip to the actual node. Update its
604        // line number if needed.
605        while (cur.lineObj != line) cur = rm(cur);
606        if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
607          setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
608        cur = cur.nextSibling;
609      } else {
610        // For lines with widgets, make an attempt to find and reuse
611        // the existing element, so that widgets aren't needlessly
612        // removed and re-inserted into the dom
613        if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
614          if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
615        // This line needs to be generated.
616        var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
617        if (lineNode != reuse) {
618          container.insertBefore(lineNode, cur);
619        } else {
620          while (cur != reuse) cur = rm(cur);
621          cur = cur.nextSibling;
622        }
623
624        lineNode.lineObj = line;
625      }
626      ++lineN;
627    });
628    while (cur) cur = rm(cur);
629  }
630
631  function buildLineElement(cm, line, lineNo, dims, reuse) {
632    var lineElement = lineContent(cm, line);
633    var markers = line.gutterMarkers, display = cm.display, wrap;
634
635    if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
636      return lineElement;
637
638    // Lines with gutter elements, widgets or a background class need
639    // to be wrapped again, and have the extra elements added to the
640    // wrapper div
641
642    if (reuse) {
643      reuse.alignable = null;
644      var isOk = true, widgetsSeen = 0;
645      for (var n = reuse.firstChild, next; n; n = next) {
646        next = n.nextSibling;
647        if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
648          reuse.removeChild(n);
649        } else {
650          for (var i = 0, first = true; i < line.widgets.length; ++i) {
651            var widget = line.widgets[i], isFirst = false;
652            if (!widget.above) { isFirst = first; first = false; }
653            if (widget.node == n.firstChild) {
654              positionLineWidget(widget, n, reuse, dims);
655              ++widgetsSeen;
656              if (isFirst) reuse.insertBefore(lineElement, n);
657              break;
658            }
659          }
660          if (i == line.widgets.length) { isOk = false; break; }
661        }
662      }
663      if (isOk && widgetsSeen == line.widgets.length) {
664        wrap = reuse;
665        reuse.className = line.wrapClass || "";
666      }
667    }
668    if (!wrap) {
669      wrap = elt("div", null, line.wrapClass, "position: relative");
670      wrap.appendChild(lineElement);
671    }
672    // Kludge to make sure the styled element lies behind the selection (by z-index)
673    if (line.bgClass)
674      wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
675    if (cm.options.lineNumbers || markers) {
676      var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
677                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
678                                         wrap.firstChild);
679      if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
680      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
681        wrap.lineNumber = gutterWrap.appendChild(
682          elt("div", lineNumberFor(cm.options, lineNo),
683              "CodeMirror-linenumber CodeMirror-gutter-elt",
684              "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
685              + display.lineNumInnerWidth + "px"));
686      if (markers)
687        for (var k = 0; k < cm.options.gutters.length; ++k) {
688          var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
689          if (found)
690            gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
691                                       dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
692        }
693    }
694    if (ie_lt8) wrap.style.zIndex = 2;
695    if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
696      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
697      positionLineWidget(widget, node, wrap, dims);
698      if (widget.above)
699        wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
700      else
701        wrap.appendChild(node);
702      signalLater(widget, "redraw");
703    }
704    return wrap;
705  }
706
707  function positionLineWidget(widget, node, wrap, dims) {
708    if (widget.noHScroll) {
709      (wrap.alignable || (wrap.alignable = [])).push(node);
710      var width = dims.wrapperWidth;
711      node.style.left = dims.fixedPos + "px";
712      if (!widget.coverGutter) {
713        width -= dims.gutterTotalWidth;
714        node.style.paddingLeft = dims.gutterTotalWidth + "px";
715      }
716      node.style.width = width + "px";
717    }
718    if (widget.coverGutter) {
719      node.style.zIndex = 5;
720      node.style.position = "relative";
721      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
722    }
723  }
724
725  // SELECTION / CURSOR
726
727  function updateSelection(cm) {
728    var display = cm.display;
729    var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
730    if (collapsed || cm.options.showCursorWhenSelecting)
731      updateSelectionCursor(cm);
732    else
733      display.cursor.style.display = display.otherCursor.style.display = "none";
734    if (!collapsed)
735      updateSelectionRange(cm);
736    else
737      display.selectionDiv.style.display = "none";
738
739    // Move the hidden textarea near the cursor to prevent scrolling artifacts
740    var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
741    var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
742    display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
743                                                      headPos.top + lineOff.top - wrapOff.top)) + "px";
744    display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
745                                                       headPos.left + lineOff.left - wrapOff.left)) + "px";
746  }
747
748  // No selection, plain cursor
749  function updateSelectionCursor(cm) {
750    var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
751    display.cursor.style.left = pos.left + "px";
752    display.cursor.style.top = pos.top + "px";
753    display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
754    display.cursor.style.display = "";
755
756    if (pos.other) {
757      display.otherCursor.style.display = "";
758      display.otherCursor.style.left = pos.other.left + "px";
759      display.otherCursor.style.top = pos.other.top + "px";
760      display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
761    } else { display.otherCursor.style.display = "none"; }
762  }
763
764  // Highlight selection
765  function updateSelectionRange(cm) {
766    var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
767    var fragment = document.createDocumentFragment();
768    var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
769
770    function add(left, top, width, bottom) {
771      if (top < 0) top = 0;
772      fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
773                               "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
774                               "px; height: " + (bottom - top) + "px"));
775    }
776
777    function drawForLine(line, fromArg, toArg, retTop) {
778      var lineObj = getLine(doc, line);
779      var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
780      function coords(ch) {
781        return charCoords(cm, Pos(line, ch), "div", lineObj);
782      }
783
784      iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
785        var leftPos = coords(dir == "rtl" ? to - 1 : from);
786        var rightPos = coords(dir == "rtl" ? from : to - 1);
787        var left = leftPos.left, right = rightPos.right;
788        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
789          add(left, leftPos.top, null, leftPos.bottom);
790          left = pl;
791          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
792        }
793        if (toArg == null && to == lineLen) right = clientWidth;
794        if (fromArg == null && from == 0) left = pl;
795        rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
796        if (left < pl + 1) left = pl;
797        add(left, rightPos.top, right - left, rightPos.bottom);
798      });
799      return rVal;
800    }
801
802    if (sel.from.line == sel.to.line) {
803      drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
804    } else {
805      var fromObj = getLine(doc, sel.from.line);
806      var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
807      while (merged = collapsedSpanAtEnd(cur)) {
808        var found = merged.find();
809        path.push(found.from.ch, found.to.line, found.to.ch);
810        if (found.to.line == sel.to.line) {
811          path.push(sel.to.ch);
812          singleLine = true;
813          break;
814        }
815        cur = getLine(doc, found.to.line);
816      }
817
818      // This is a single, merged line
819      if (singleLine) {
820        for (var i = 0; i < path.length; i += 3)
821          drawForLine(path[i], path[i+1], path[i+2]);
822      } else {
823        var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
824        if (sel.from.ch)
825          // Draw the first line of selection.
826          middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
827        else
828          // Simply include it in the middle block.
829          middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
830
831        if (!sel.to.ch)
832          middleBot = heightAtLine(cm, toObj) - display.viewOffset;
833        else
834          middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
835
836        if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
837      }
838    }
839
840    removeChildrenAndAdd(display.selectionDiv, fragment);
841    display.selectionDiv.style.display = "";
842  }
843
844  // Cursor-blinking
845  function restartBlink(cm) {
846    var display = cm.display;
847    clearInterval(display.blinker);
848    var on = true;
849    display.cursor.style.visibility = display.otherCursor.style.visibility = "";
850    display.blinker = setInterval(function() {
851      if (!display.cursor.offsetHeight) return;
852      display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
853    }, cm.options.cursorBlinkRate);
854  }
855
856  // HIGHLIGHT WORKER
857
858  function startWorker(cm, time) {
859    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
860      cm.state.highlight.set(time, bind(highlightWorker, cm));
861  }
862
863  function highlightWorker(cm) {
864    var doc = cm.doc;
865    if (doc.frontier < doc.first) doc.frontier = doc.first;
866    if (doc.frontier >= cm.display.showingTo) return;
867    var end = +new Date + cm.options.workTime;
868    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
869    var changed = [], prevChange;
870    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
871      if (doc.frontier >= cm.display.showingFrom) { // Visible
872        var oldStyles = line.styles;
873        line.styles = highlightLine(cm, line, state);
874        var ischange = !oldStyles || oldStyles.length != line.styles.length;
875        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
876        if (ischange) {
877          if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
878          else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
879        }
880        line.stateAfter = copyState(doc.mode, state);
881      } else {
882        processLine(cm, line, state);
883        line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
884      }
885      ++doc.frontier;
886      if (+new Date > end) {
887        startWorker(cm, cm.options.workDelay);
888        return true;
889      }
890    });
891    if (changed.length)
892      operation(cm, function() {
893        for (var i = 0; i < changed.length; ++i)
894          regChange(this, changed[i].start, changed[i].end);
895      })();
896  }
897
898  // Finds the line to start with when starting a parse. Tries to
899  // find a line with a stateAfter, so that it can start with a
900  // valid state. If that fails, it returns the line with the
901  // smallest indentation, which tends to need the least context to
902  // parse correctly.
903  function findStartLine(cm, n) {
904    var minindent, minline, doc = cm.doc;
905    for (var search = n, lim = n - 100; search > lim; --search) {
906      if (search <= doc.first) return doc.first;
907      var line = getLine(doc, search - 1);
908      if (line.stateAfter) return search;
909      var indented = countColumn(line.text, null, cm.options.tabSize);
910      if (minline == null || minindent > indented) {
911        minline = search - 1;
912        minindent = indented;
913      }
914    }
915    return minline;
916  }
917
918  function getStateBefore(cm, n) {
919    var doc = cm.doc, display = cm.display;
920      if (!doc.mode.startState) return true;
921    var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
922    if (!state) state = startState(doc.mode);
923    else state = copyState(doc.mode, state);
924    doc.iter(pos, n, function(line) {
925      processLine(cm, line, state);
926      var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
927      line.stateAfter = save ? copyState(doc.mode, state) : null;
928      ++pos;
929    });
930    return state;
931  }
932
933  // POSITION MEASUREMENT
934
935  function paddingTop(display) {return display.lineSpace.offsetTop;}
936  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
937  function paddingLeft(display) {
938    var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
939    return e.offsetLeft;
940  }
941
942  function measureChar(cm, line, ch, data) {
943    var dir = -1;
944    data = data || measureLine(cm, line);
945
946    for (var pos = ch;; pos += dir) {
947      var r = data[pos];
948      if (r) break;
949      if (dir < 0 && pos == 0) dir = 1;
950    }
951    return {left: pos < ch ? r.right : r.left,
952            right: pos > ch ? r.left : r.right,
953            top: r.top, bottom: r.bottom};
954  }
955
956  function findCachedMeasurement(cm, line) {
957    var cache = cm.display.measureLineCache;
958    for (var i = 0; i < cache.length; ++i) {
959      var memo = cache[i];
960      if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
961          cm.display.scroller.clientWidth == memo.width &&
962          memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
963        return memo.measure;
964    }
965  }
966
967  function measureLine(cm, line) {
968    // First look in the cache
969    var measure = findCachedMeasurement(cm, line);
970    if (!measure) {
971      // Failing that, recompute and store result in cache
972      measure = measureLineInner(cm, line);
973      var cache = cm.display.measureLineCache;
974      var memo = {text: line.text, width: cm.display.scroller.clientWidth,
975                  markedSpans: line.markedSpans, measure: measure,
976                  classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
977      if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
978      else cache.push(memo);
979    }
980    return measure;
981  }
982
983  function measureLineInner(cm, line) {
984    var display = cm.display, measure = emptyArray(line.text.length);
985    var pre = lineContent(cm, line, measure);
986
987    // IE does not cache element positions of inline elements between
988    // calls to getBoundingClientRect. This makes the loop below,
989    // which gathers the positions of all the characters on the line,
990    // do an amount of layout work quadratic to the number of
991    // characters. When line wrapping is off, we try to improve things
992    // by first subdividing the line into a bunch of inline blocks, so
993    // that IE can reuse most of the layout information from caches
994    // for those blocks. This does interfere with line wrapping, so it
995    // doesn't work when wrapping is on, but in that case the
996    // situation is slightly better, since IE does cache line-wrapping
997    // information and only recomputes per-line.
998    if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
999      var fragment = document.createDocumentFragment();
1000      var chunk = 10, n = pre.childNodes.length;
1001      for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1002        var wrap = elt("div", null, null, "display: inline-block");
1003        for (var j = 0; j < chunk && n; ++j) {
1004          wrap.appendChild(pre.firstChild);
1005          --n;
1006        }
1007        fragment.appendChild(wrap);
1008      }
1009      pre.appendChild(fragment);
1010    }
1011
1012    removeChildrenAndAdd(display.measure, pre);
1013
1014    var outer = getRect(display.lineDiv);
1015    var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1016    // Work around an IE7/8 bug where it will sometimes have randomly
1017    // replaced our pre with a clone at this point.
1018    if (ie_lt9 && display.measure.first != pre)
1019      removeChildrenAndAdd(display.measure, pre);
1020
1021    for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1022      var size = getRect(cur);
1023      var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1024      for (var j = 0; j < vranges.length; j += 2) {
1025        var rtop = vranges[j], rbot = vranges[j+1];
1026        if (rtop > bot || rbot < top) continue;
1027        if (rtop <= top && rbot >= bot ||
1028            top <= rtop && bot >= rbot ||
1029            Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1030          vranges[j] = Math.min(top, rtop);
1031          vranges[j+1] = Math.max(bot, rbot);
1032          break;
1033        }
1034      }
1035      if (j == vranges.length) vranges.push(top, bot);
1036      var right = size.right;
1037      if (cur.measureRight) right = getRect(cur.measureRight).left;
1038      data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
1039    }
1040    for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1041      var vr = cur.top;
1042      cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1043    }
1044
1045    return data;
1046  }
1047
1048  function measureLineWidth(cm, line) {
1049    var hasBadSpan = false;
1050    if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1051      var sp = line.markedSpans[i];
1052      if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1053    }
1054    var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1055    if (cached) return measureChar(cm, line, line.text.length, cached).right;
1056
1057    var pre = lineContent(cm, line);
1058    var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1059    removeChildrenAndAdd(cm.display.measure, pre);
1060    return getRect(end).right - getRect(cm.display.lineDiv).left;
1061  }
1062
1063  function clearCaches(cm) {
1064    cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1065    cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1066    cm.display.maxLineChanged = true;
1067    cm.display.lineNumChars = null;
1068  }
1069
1070  // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1071  function intoCoordSystem(cm, lineObj, rect, context) {
1072    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1073      var size = widgetHeight(lineObj.widgets[i]);
1074      rect.top += size; rect.bottom += size;
1075    }
1076    if (context == "line") return rect;
1077    if (!context) context = "local";
1078    var yOff = heightAtLine(cm, lineObj);
1079    if (context != "local") yOff -= cm.display.viewOffset;
1080    if (context == "page") {
1081      var lOff = getRect(cm.display.lineSpace);
1082      yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1083      var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1084      rect.left += xOff; rect.right += xOff;
1085    }
1086    rect.top += yOff; rect.bottom += yOff;
1087    return rect;
1088  }
1089
1090  // Context may be "window", "page", "div", or "local"/null
1091  // Result is in local coords
1092  function fromCoordSystem(cm, coords, context) {
1093    if (context == "div") return coords;
1094    var left = coords.left, top = coords.top;
1095    if (context == "page") {
1096      left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
1097      top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
1098    }
1099    var lineSpaceBox = getRect(cm.display.lineSpace);
1100    left -= lineSpaceBox.left;
1101    top -= lineSpaceBox.top;
1102    if (context == "local" || !context) {
1103      var editorBox = getRect(cm.display.wrapper);
1104      left -= editorBox.left;
1105      top -= editorBox.top;
1106    }
1107    return {left: left, top: top};
1108  }
1109
1110  function charCoords(cm, pos, context, lineObj) {
1111    if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1112    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1113  }
1114
1115  function cursorCoords(cm, pos, context, lineObj, measurement) {
1116    lineObj = lineObj || getLine(cm.doc, pos.line);
1117    if (!measurement) measurement = measureLine(cm, lineObj);
1118    function get(ch, right) {
1119      var m = measureChar(cm, lineObj, ch, measurement);
1120      if (right) m.left = m.right; else m.right = m.left;
1121      return intoCoordSystem(cm, lineObj, m, context);
1122    }
1123    var order = getOrder(lineObj), ch = pos.ch;
1124    if (!order) return get(ch);
1125    var main, other, linedir = order[0].level;
1126    for (var i = 0; i < order.length; ++i) {
1127      var part = order[i], rtl = part.level % 2, nb, here;
1128      if (part.from < ch && part.to > ch) return get(ch, rtl);
1129      var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1130      if (left == ch) {
1131        // IE returns bogus offsets and widths for edges where the
1132        // direction flips, but only for the side with the lower
1133        // level. So we try to use the side with the higher level.
1134        if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1135        else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1136        if (rtl == linedir) main = here; else other = here;
1137      } else if (right == ch) {
1138        var nb = i < order.length - 1 && order[i+1];
1139        if (!rtl && nb && nb.from == nb.to) continue;
1140        if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1141        else here = get(rtl ? ch : ch - 1, true);
1142        if (rtl == linedir) main = here; else other = here;
1143      }
1144    }
1145    if (linedir && !ch) other = get(order[0].to - 1);
1146    if (!main) return other;
1147    if (other) main.other = other;
1148    return main;
1149  }
1150
1151  function PosMaybeOutside(line, ch, outside) {
1152    var pos = new Pos(line, ch);
1153    if (outside) pos.outside = true;
1154    return pos;
1155  }
1156
1157  // Coords must be lineSpace-local
1158  function coordsChar(cm, x, y) {
1159    var doc = cm.doc;
1160    y += cm.display.viewOffset;
1161    if (y < 0) return PosMaybeOutside(doc.first, 0, true);
1162    var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1163    if (lineNo > last)
1164      return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
1165    if (x < 0) x = 0;
1166
1167    for (;;) {
1168      var lineObj = getLine(doc, lineNo);
1169      var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1170      var merged = collapsedSpanAtEnd(lineObj);
1171      var mergedPos = merged && merged.find();
1172      if (merged && found.ch >= mergedPos.from.ch)
1173        lineNo = mergedPos.to.line;
1174      else
1175        return found;
1176    }
1177  }
1178
1179  function coordsCharInner(cm, lineObj, lineNo, x, y) {
1180    var innerOff = y - heightAtLine(cm, lineObj);
1181    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1182    var measurement = measureLine(cm, lineObj);
1183
1184    function getX(ch) {
1185      var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1186                            lineObj, measurement);
1187      wrongLine = true;
1188      if (innerOff > sp.bottom) return sp.left - adjust;
1189      else if (innerOff < sp.top) return sp.left + adjust;
1190      else wrongLine = false;
1191      return sp.left;
1192    }
1193
1194    var bidi = getOrder(lineObj), dist = lineObj.text.length;
1195    var from = lineLeft(lineObj), to = lineRight(lineObj);
1196    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1197
1198    if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
1199    // Do a binary search between these bounds.
1200    for (;;) {
1201      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1202        var after = x - fromX < toX - x, ch = after ? from : to;
1203        while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1204        var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
1205        pos.after = after;
1206        return pos;
1207      }
1208      var step = Math.ceil(dist / 2), middle = from + step;
1209      if (bidi) {
1210        middle = from;
1211        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1212      }
1213      var middleX = getX(middle);
1214      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;}
1215      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;}
1216    }
1217  }
1218
1219  var measureText;
1220  function textHeight(display) {
1221    if (display.cachedTextHeight != null) return display.cachedTextHeight;
1222    if (measureText == null) {
1223      measureText = elt("pre");
1224      // Measure a bunch of lines, for browsers that compute
1225      // fractional heights.
1226      for (var i = 0; i < 49; ++i) {
1227        measureText.appendChild(document.createTextNode("x"));
1228        measureText.appendChild(elt("br"));
1229      }
1230      measureText.appendChild(document.createTextNode("x"));
1231    }
1232    removeChildrenAndAdd(display.measure, measureText);
1233    var height = measureText.offsetHeight / 50;
1234    if (height > 3) display.cachedTextHeight = height;
1235    removeChildren(display.measure);
1236    return height || 1;
1237  }
1238
1239  function charWidth(display) {
1240    if (display.cachedCharWidth != null) return display.cachedCharWidth;
1241    var anchor = elt("span", "x");
1242    var pre = elt("pre", [anchor]);
1243    removeChildrenAndAdd(display.measure, pre);
1244    var width = anchor.offsetWidth;
1245    if (width > 2) display.cachedCharWidth = width;
1246    return width || 10;
1247  }
1248
1249  // OPERATIONS
1250
1251  // Operations are used to wrap changes in such a way that each
1252  // change won't have to update the cursor and display (which would
1253  // be awkward, slow, and error-prone), but instead updates are
1254  // batched and then all combined and executed at once.
1255
1256  var nextOpId = 0;
1257  function startOperation(cm) {
1258    cm.curOp = {
1259      // An array of ranges of lines that have to be updated. See
1260      // updateDisplay.
1261      changes: [],
1262      updateInput: null,
1263      userSelChange: null,
1264      textChanged: null,
1265      selectionChanged: false,
1266      updateMaxLine: false,
1267      updateScrollPos: false,
1268      id: ++nextOpId
1269    };
1270    if (!delayedCallbackDepth++) delayedCallbacks = [];
1271  }
1272
1273  function endOperation(cm) {
1274    var op = cm.curOp, doc = cm.doc, display = cm.display;
1275    cm.curOp = null;
1276
1277    if (op.updateMaxLine) computeMaxLength(cm);
1278    if (display.maxLineChanged && !cm.options.lineWrapping) {
1279      var width = measureLineWidth(cm, display.maxLine);
1280      display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1281      display.maxLineChanged = false;
1282      var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1283      if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1284        setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1285    }
1286    var newScrollPos, updated;
1287    if (op.updateScrollPos) {
1288      newScrollPos = op.updateScrollPos;
1289    } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1290      var coords = cursorCoords(cm, doc.sel.head);
1291      newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1292    }
1293    if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
1294      updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1295      if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1296    }
1297    if (!updated && op.selectionChanged) updateSelection(cm);
1298    if (op.updateScrollPos) {
1299      display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1300      display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1301      alignHorizontally(cm);
1302    } else if (newScrollPos) {
1303      scrollCursorIntoView(cm);
1304    }
1305    if (op.selectionChanged) restartBlink(cm);
1306
1307    if (cm.state.focused && op.updateInput)
1308      resetInput(cm, op.userSelChange);
1309
1310    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1311    if (hidden) for (var i = 0; i < hidden.length; ++i)
1312      if (!hidden[i].lines.length) signal(hidden[i], "hide");
1313    if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1314      if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1315
1316    var delayed;
1317    if (!--delayedCallbackDepth) {
1318      delayed = delayedCallbacks;
1319      delayedCallbacks = null;
1320    }
1321    if (op.textChanged)
1322      signal(cm, "change", cm, op.textChanged);
1323    if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1324    if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1325  }
1326
1327  // Wraps a function in an operation. Returns the wrapped function.
1328  function operation(cm1, f) {
1329    return function() {
1330      var cm = cm1 || this, withOp = !cm.curOp;
1331      if (withOp) startOperation(cm);
1332      try { var result = f.apply(cm, arguments); }
1333      finally { if (withOp) endOperation(cm); }
1334      return result;
1335    };
1336  }
1337  function docOperation(f) {
1338    return function() {
1339      var withOp = this.cm && !this.cm.curOp, result;
1340      if (withOp) startOperation(this.cm);
1341      try { result = f.apply(this, arguments); }
1342      finally { if (withOp) endOperation(this.cm); }
1343      return result;
1344    };
1345  }
1346  function runInOp(cm, f) {
1347    var withOp = !cm.curOp, result;
1348    if (withOp) startOperation(cm);
1349    try { result = f(); }
1350    finally { if (withOp) endOperation(cm); }
1351    return result;
1352  }
1353
1354  function regChange(cm, from, to, lendiff) {
1355    if (from == null) from = cm.doc.first;
1356    if (to == null) to = cm.doc.first + cm.doc.size;
1357    cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1358  }
1359
1360  // INPUT HANDLING
1361
1362  function slowPoll(cm) {
1363    if (cm.display.pollingFast) return;
1364    cm.display.poll.set(cm.options.pollInterval, function() {
1365      readInput(cm);
1366      if (cm.state.focused) slowPoll(cm);
1367    });
1368  }
1369
1370  function fastPoll(cm) {
1371    var missed = false;
1372    cm.display.pollingFast = true;
1373    function p() {
1374      var changed = readInput(cm);
1375      if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1376      else {cm.display.pollingFast = false; slowPoll(cm);}
1377    }
1378    cm.display.poll.set(20, p);
1379  }
1380
1381  // prevInput is a hack to work with IME. If we reset the textarea
1382  // on every change, that breaks IME. So we look for changes
1383  // compared to the previous content instead. (Modern browsers have
1384  // events that indicate IME taking place, but these are not widely
1385  // supported or compatible enough yet to rely on.)
1386  function readInput(cm) {
1387    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1388    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
1389    var text = input.value;
1390    if (text == prevInput && posEq(sel.from, sel.to)) return false;
1391    // IE enjoys randomly deselecting our input's text when
1392    // re-focusing. If the selection is gone but the cursor is at the
1393    // start of the input, that's probably what happened.
1394    if (ie && text && input.selectionStart === 0) {
1395      resetInput(cm, true);
1396      return false;
1397    }
1398    var withOp = !cm.curOp;
1399    if (withOp) startOperation(cm);
1400    sel.shift = false;
1401    var same = 0, l = Math.min(prevInput.length, text.length);
1402    while (same < l && prevInput[same] == text[same]) ++same;
1403    var from = sel.from, to = sel.to;
1404    if (same < prevInput.length)
1405      from = Pos(from.line, from.ch - (prevInput.length - same));
1406    else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1407      to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1408    var updateInput = cm.curOp.updateInput;
1409    makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
1410                        origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
1411
1412    cm.curOp.updateInput = updateInput;
1413    if (text.length > 1000) input.value = cm.display.prevInput = "";
1414    else cm.display.prevInput = text;
1415    if (withOp) endOperation(cm);
1416    cm.state.pasteIncoming = false;
1417    return true;
1418  }
1419
1420  function resetInput(cm, user) {
1421    var minimal, selected, doc = cm.doc;
1422    if (!posEq(doc.sel.from, doc.sel.to)) {
1423      cm.display.prevInput = "";
1424      minimal = hasCopyEvent &&
1425        (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1426      if (minimal) cm.display.input.value = "-";
1427      else cm.display.input.value = selected || cm.getSelection();
1428      if (cm.state.focused) selectInput(cm.display.input);
1429    } else if (user) cm.display.prevInput = cm.display.input.value = "";
1430    cm.display.inaccurateSelection = minimal;
1431  }
1432
1433  function focusInput(cm) {
1434    if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1435      cm.display.input.focus();
1436  }
1437
1438  function isReadOnly(cm) {
1439    return cm.options.readOnly || cm.doc.cantEdit;
1440  }
1441
1442  // EVENT HANDLERS
1443
1444  function registerEventHandlers(cm) {
1445    var d = cm.display;
1446    on(d.scroller, "mousedown", operation(cm, onMouseDown));
1447    on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1448    on(d.lineSpace, "selectstart", function(e) {
1449      if (!eventInWidget(d, e)) e_preventDefault(e);
1450    });
1451    // Gecko browsers fire contextmenu *after* opening the menu, at
1452    // which point we can't mess with it anymore. Context menu is
1453    // handled in onMouseDown for Gecko.
1454    if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1455
1456    on(d.scroller, "scroll", function() {
1457      if (d.scroller.clientHeight) {
1458        setScrollTop(cm, d.scroller.scrollTop);
1459        setScrollLeft(cm, d.scroller.scrollLeft, true);
1460        signal(cm, "scroll", cm);
1461      }
1462    });
1463    on(d.scrollbarV, "scroll", function() {
1464      if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1465    });
1466    on(d.scrollbarH, "scroll", function() {
1467      if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1468    });
1469
1470    on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1471    on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1472
1473    function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1474    on(d.scrollbarH, "mousedown", reFocus);
1475    on(d.scrollbarV, "mousedown", reFocus);
1476    // Prevent wrapper from ever scrolling
1477    on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1478
1479    function onResize() {
1480      // Might be a text scaling operation, clear size caches.
1481      d.cachedCharWidth = d.cachedTextHeight = null;
1482      clearCaches(cm);
1483      runInOp(cm, bind(regChange, cm));
1484    }
1485    on(window, "resize", onResize);
1486    // Above handler holds on to the editor and its data structures.
1487    // Here we poll to unregister it when the editor is no longer in
1488    // the document, so that it can be garbage-collected.
1489    function unregister() {
1490      for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1491      if (p) setTimeout(unregister, 5000);
1492      else off(window, "resize", onResize);
1493    }
1494    setTimeout(unregister, 5000);
1495
1496    on(d.input, "keyup", operation(cm, function(e) {
1497      if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1498      if (e.keyCode == 16) cm.doc.sel.shift = false;
1499    }));
1500    on(d.input, "input", bind(fastPoll, cm));
1501    on(d.input, "keydown", operation(cm, onKeyDown));
1502    on(d.input, "keypress", operation(cm, onKeyPress));
1503    on(d.input, "focus", bind(onFocus, cm));
1504    on(d.input, "blur", bind(onBlur, cm));
1505
1506    function drag_(e) {
1507      if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1508      e_stop(e);
1509    }
1510    if (cm.options.dragDrop) {
1511      on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1512      on(d.scroller, "dragenter", drag_);
1513      on(d.scroller, "dragover", drag_);
1514      on(d.scroller, "drop", operation(cm, onDrop));
1515    }
1516    on(d.scroller, "paste", function(e){
1517      if (eventInWidget(d, e)) return;
1518      focusInput(cm);
1519      fastPoll(cm);
1520    });
1521    on(d.input, "paste", function() {
1522      cm.state.pasteIncoming = true;
1523      fastPoll(cm);
1524    });
1525
1526    function prepareCopy() {
1527      if (d.inaccurateSelection) {
1528        d.prevInput = "";
1529        d.inaccurateSelection = false;
1530        d.input.value = cm.getSelection();
1531        selectInput(d.input);
1532      }
1533    }
1534    on(d.input, "cut", prepareCopy);
1535    on(d.input, "copy", prepareCopy);
1536
1537    // Needed to handle Tab key in KHTML
1538    if (khtml) on(d.sizer, "mouseup", function() {
1539        if (document.activeElement == d.input) d.input.blur();
1540        focusInput(cm);
1541    });
1542  }
1543
1544  function eventInWidget(display, e) {
1545    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1546      if (!n) return true;
1547      if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1548          n.parentNode == display.sizer && n != display.mover) return true;
1549    }
1550  }
1551
1552  function posFromMouse(cm, e, liberal) {
1553    var display = cm.display;
1554    if (!liberal) {
1555      var target = e_target(e);
1556      if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1557          target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1558          target == display.scrollbarFiller) return null;
1559    }
1560    var x, y, space = getRect(display.lineSpace);
1561    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1562    try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1563    return coordsChar(cm, x - space.left, y - space.top);
1564  }
1565
1566  var lastClick, lastDoubleClick;
1567  function onMouseDown(e) {
1568    var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1569    sel.shift = e.shiftKey;
1570
1571    if (eventInWidget(display, e)) {
1572      if (!webkit) {
1573        display.scroller.draggable = false;
1574        setTimeout(function(){display.scroller.draggable = true;}, 100);
1575      }
1576      return;
1577    }
1578    if (clickInGutter(cm, e)) return;
1579    var start = posFromMouse(cm, e);
1580
1581    switch (e_button(e)) {
1582    case 3:
1583      if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1584      return;
1585    case 2:
1586      if (start) extendSelection(cm.doc, start);
1587      setTimeout(bind(focusInput, cm), 20);
1588      e_preventDefault(e);
1589      return;
1590    }
1591    // For button 1, if it was clicked inside the editor
1592    // (posFromMouse returning non-null), we have to adjust the
1593    // selection.
1594    if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1595
1596    if (!cm.state.focused) onFocus(cm);
1597
1598    var now = +new Date, type = "single";
1599    if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1600      type = "triple";
1601      e_preventDefault(e);
1602      setTimeout(bind(focusInput, cm), 20);
1603      selectLine(cm, start.line);
1604    } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1605      type = "double";
1606      lastDoubleClick = {time: now, pos: start};
1607      e_preventDefault(e);
1608      var word = findWordAt(getLine(doc, start.line).text, start);
1609      extendSelection(cm.doc, word.from, word.to);
1610    } else { lastClick = {time: now, pos: start}; }
1611
1612    var last = start;
1613    if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1614        !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1615      var dragEnd = operation(cm, function(e2) {
1616        if (webkit) display.scroller.draggable = false;
1617        cm.state.draggingText = false;
1618        off(document, "mouseup", dragEnd);
1619        off(display.scroller, "drop", dragEnd);
1620        if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1621          e_preventDefault(e2);
1622          extendSelection(cm.doc, start);
1623          focusInput(cm);
1624        }
1625      });
1626      // Let the drag handler handle this.
1627      if (webkit) display.scroller.draggable = true;
1628      cm.state.draggingText = dragEnd;
1629      // IE's approach to draggable
1630      if (display.scroller.dragDrop) display.scroller.dragDrop();
1631      on(document, "mouseup", dragEnd);
1632      on(display.scroller, "drop", dragEnd);
1633      return;
1634    }
1635    e_preventDefault(e);
1636    if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1637
1638    var startstart = sel.from, startend = sel.to;
1639
1640    function doSelect(cur) {
1641      if (type == "single") {
1642        extendSelection(cm.doc, clipPos(doc, start), cur);
1643        return;
1644      }
1645
1646      startstart = clipPos(doc, startstart);
1647      startend = clipPos(doc, startend);
1648      if (type == "double") {
1649        var word = findWordAt(getLine(doc, cur.line).text, cur);
1650        if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1651        else extendSelection(cm.doc, startstart, word.to);
1652      } else if (type == "triple") {
1653        if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1654        else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1655      }
1656    }
1657
1658    var editorSize = getRect(display.wrapper);
1659    // Used to ensure timeout re-tries don't fire when another extend
1660    // happened in the meantime (clearTimeout isn't reliable -- at
1661    // least on Chrome, the timeouts still happen even when cleared,
1662    // if the clear happens after their scheduled firing time).
1663    var counter = 0;
1664
1665    function extend(e) {
1666      var curCount = ++counter;
1667      var cur = posFromMouse(cm, e, true);
1668      if (!cur) return;
1669      if (!posEq(cur, last)) {
1670        if (!cm.state.focused) onFocus(cm);
1671        last = cur;
1672        doSelect(cur);
1673        var visible = visibleLines(display, doc);
1674        if (cur.line >= visible.to || cur.line < visible.from)
1675          setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1676      } else {
1677        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1678        if (outside) setTimeout(operation(cm, function() {
1679          if (counter != curCount) return;
1680          display.scroller.scrollTop += outside;
1681          extend(e);
1682        }), 50);
1683      }
1684    }
1685
1686    function done(e) {
1687      counter = Infinity;
1688      var cur = posFromMouse(cm, e);
1689      if (cur) doSelect(cur);
1690      e_preventDefault(e);
1691      focusInput(cm);
1692      off(document, "mousemove", move);
1693      off(document, "mouseup", up);
1694    }
1695
1696    var move = operation(cm, function(e) {
1697      if (!ie && !e_button(e)) done(e);
1698      else extend(e);
1699    });
1700    var up = operation(cm, done);
1701    on(document, "mousemove", move);
1702    on(document, "mouseup", up);
1703  }
1704
1705  function onDrop(e) {
1706    var cm = this;
1707    if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1708      return;
1709    e_preventDefault(e);
1710    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1711    if (!pos || isReadOnly(cm)) return;
1712    if (files && files.length && window.FileReader && window.File) {
1713      var n = files.length, text = Array(n), read = 0;
1714      var loadFile = function(file, i) {
1715        var reader = new FileReader;
1716        reader.onload = function() {
1717          text[i] = reader.result;
1718          if (++read == n) {
1719            pos = clipPos(cm.doc, pos);
1720            makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1721          }
1722        };
1723        reader.readAsText(file);
1724      };
1725      for (var i = 0; i < n; ++i) loadFile(files[i], i);
1726    } else {
1727      // Don't do a replace if the drop happened inside of the selected text.
1728      if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1729        cm.state.draggingText(e);
1730        // Ensure the editor is re-focused
1731        setTimeout(bind(focusInput, cm), 20);
1732        return;
1733      }
1734      try {
1735        var text = e.dataTransfer.getData("Text");
1736        if (text) {
1737          var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1738          setSelection(cm.doc, pos, pos);
1739          if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1740          cm.replaceSelection(text, null, "paste");
1741          focusInput(cm);
1742          onFocus(cm);
1743        }
1744      }
1745      catch(e){}
1746    }
1747  }
1748
1749  function clickInGutter(cm, e) {
1750    var display = cm.display;
1751    try { var mX = e.clientX, mY = e.clientY; }
1752    catch(e) { return false; }
1753
1754    if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1755    e_preventDefault(e);
1756    if (!hasHandler(cm, "gutterClick")) return true;
1757
1758    var lineBox = getRect(display.lineDiv);
1759    if (mY > lineBox.bottom) return true;
1760    mY -= lineBox.top - display.viewOffset;
1761
1762    for (var i = 0; i < cm.options.gutters.length; ++i) {
1763      var g = display.gutters.childNodes[i];
1764      if (g && getRect(g).right >= mX) {
1765        var line = lineAtHeight(cm.doc, mY);
1766        var gutter = cm.options.gutters[i];
1767        signalLater(cm, "gutterClick", cm, line, gutter, e);
1768        break;
1769      }
1770    }
1771    return true;
1772  }
1773
1774  function onDragStart(cm, e) {
1775    if (eventInWidget(cm.display, e)) return;
1776
1777    var txt = cm.getSelection();
1778    e.dataTransfer.setData("Text", txt);
1779
1780    // Use dummy image instead of default browsers image.
1781    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1782    if (e.dataTransfer.setDragImage) {
1783      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1784      if (opera) {
1785        img.width = img.height = 1;
1786        cm.display.wrapper.appendChild(img);
1787        // Force a relayout, or Opera won't use our image for some obscure reason
1788        img._top = img.offsetTop;
1789      }
1790      if (safari) {
1791        if (cm.display.dragImg) {
1792          img = cm.display.dragImg;
1793        } else {
1794          cm.display.dragImg = img;
1795          img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1796          cm.display.wrapper.appendChild(img);
1797        }
1798      }
1799      e.dataTransfer.setDragImage(img, 0, 0);
1800      if (opera) img.parentNode.removeChild(img);
1801    }
1802  }
1803
1804  function setScrollTop(cm, val) {
1805    if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1806    cm.doc.scrollTop = val;
1807    if (!gecko) updateDisplay(cm, [], val);
1808    if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1809    if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1810    if (gecko) updateDisplay(cm, []);
1811  }
1812  function setScrollLeft(cm, val, isScroller) {
1813    if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1814    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1815    cm.doc.scrollLeft = val;
1816    alignHorizontally(cm);
1817    if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1818    if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1819  }
1820
1821  // Since the delta values reported on mouse wheel events are
1822  // unstandardized between browsers and even browser versions, and
1823  // generally horribly unpredictable, this code starts by measuring
1824  // the scroll effect that the first few mouse wheel events have,
1825  // and, from that, detects the way it can convert deltas to pixel
1826  // offsets afterwards.
1827  //
1828  // The reason we want to know the amount a wheel event will scroll
1829  // is that it gives us a chance to update the display before the
1830  // actual scrolling happens, reducing flickering.
1831
1832  var wheelSamples = 0, wheelPixelsPerUnit = null;
1833  // Fill in a browser-detected starting value on browsers where we
1834  // know one. These don't have to be accurate -- the result of them
1835  // being wrong would just be a slight flicker on the first wheel
1836  // scroll (if it is large enough).
1837  if (ie) wheelPixelsPerUnit = -.53;
1838  else if (gecko) wheelPixelsPerUnit = 15;
1839  else if (chrome) wheelPixelsPerUnit = -.7;
1840  else if (safari) wheelPixelsPerUnit = -1/3;
1841
1842  function onScrollWheel(cm, e) {
1843    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1844    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1845    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1846    else if (dy == null) dy = e.wheelDelta;
1847
1848    // Webkit browsers on OS X abort momentum scrolls when the target
1849    // of the scroll event is removed from the scrollable element.
1850    // This hack (see related code in patchDisplay) makes sure the
1851    // element is kept around.
1852    if (dy && mac && webkit) {
1853      for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1854        if (cur.lineObj) {
1855          cm.display.currentWheelTarget = cur;
1856          break;
1857        }
1858      }
1859    }
1860
1861    var display = cm.display, scroll = display.scroller;
1862    // On some browsers, horizontal scrolling will cause redraws to
1863    // happen before the gutter has been realigned, causing it to
1864    // wriggle around in a most unseemly way. When we have an
1865    // estimated pixels/delta value, we just handle horizontal
1866    // scrolling entirely here. It'll be slightly off from native, but
1867    // better than glitching out.
1868    if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1869      if (dy)
1870        setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1871      setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1872      e_preventDefault(e);
1873      display.wheelStartX = null; // Abort measurement, if in progress
1874      return;
1875    }
1876
1877    if (dy && wheelPixelsPerUnit != null) {
1878      var pixels = dy * wheelPixelsPerUnit;
1879      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1880      if (pixels < 0) top = Math.max(0, top + pixels - 50);
1881      else bot = Math.min(cm.doc.height, bot + pixels + 50);
1882      updateDisplay(cm, [], {top: top, bottom: bot});
1883    }
1884
1885    if (wheelSamples < 20) {
1886      if (display.wheelStartX == null) {
1887        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1888        display.wheelDX = dx; display.wheelDY = dy;
1889        setTimeout(function() {
1890          if (display.wheelStartX == null) return;
1891          var movedX = scroll.scrollLeft - display.wheelStartX;
1892          var movedY = scroll.scrollTop - display.wheelStartY;
1893          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1894            (movedX && display.wheelDX && movedX / display.wheelDX);
1895          display.wheelStartX = display.wheelStartY = null;
1896          if (!sample) return;
1897          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1898          ++wheelSamples;
1899        }, 200);
1900      } else {
1901        display.wheelDX += dx; display.wheelDY += dy;
1902      }
1903    }
1904  }
1905
1906  function doHandleBinding(cm, bound, dropShift) {
1907    if (typeof bound == "string") {
1908      bound = commands[bound];
1909      if (!bound) return false;
1910    }
1911    // Ensure previous input has been read, so that the handler sees a
1912    // consistent view of the document
1913    if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1914    var doc = cm.doc, prevShift = doc.sel.shift, done = false;
1915    try {
1916      if (isReadOnly(cm)) cm.state.suppressEdits = true;
1917      if (dropShift) doc.sel.shift = false;
1918      done = bound(cm) != Pass;
1919    } finally {
1920      doc.sel.shift = prevShift;
1921      cm.state.suppressEdits = false;
1922    }
1923    return done;
1924  }
1925
1926  function allKeyMaps(cm) {
1927    var maps = cm.state.keyMaps.slice(0);
1928    maps.push(cm.options.keyMap);
1929    if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
1930    return maps;
1931  }
1932
1933  var maybeTransition;
1934  function handleKeyBinding(cm, e) {
1935    // Handle auto keymap transitions
1936    var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1937    clearTimeout(maybeTransition);
1938    if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1939      if (getKeyMap(cm.options.keyMap) == startMap)
1940        cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1941    }, 50);
1942
1943    var name = keyName(e, true), handled = false;
1944    if (!name) return false;
1945    var keymaps = allKeyMaps(cm);
1946
1947    if (e.shiftKey) {
1948      // First try to resolve full name (including 'Shift-'). Failing
1949      // that, see if there is a cursor-motion command (starting with
1950      // 'go') bound to the keyname without 'Shift-'.
1951      handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
1952             || lookupKey(name, keymaps, function(b) {
1953                  if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1954                });
1955    } else {
1956      handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
1957    }
1958    if (handled == "stop") handled = false;
1959
1960    if (handled) {
1961      e_preventDefault(e);
1962      restartBlink(cm);
1963      if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
1964    }
1965    return handled;
1966  }
1967
1968  function handleCharBinding(cm, e, ch) {
1969    var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
1970                            function(b) { return doHandleBinding(cm, b, true); });
1971    if (handled) {
1972      e_preventDefault(e);
1973      restartBlink(cm);
1974    }
1975    return handled;
1976  }
1977
1978  var lastStoppedKey = null;
1979  function onKeyDown(e) {
1980    var cm = this;
1981    if (!cm.state.focused) onFocus(cm);
1982    if (ie && e.keyCode == 27) { e.returnValue = false; }
1983    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1984    var code = e.keyCode;
1985    // IE does strange things with escape.
1986    cm.doc.sel.shift = code == 16 || e.shiftKey;
1987    // First give onKeyEvent option a chance to handle this.
1988    var handled = handleKeyBinding(cm, e);
1989    if (opera) {
1990      lastStoppedKey = handled ? code : null;
1991      // Opera has no cut event... we try to at least catch the key combo
1992      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
1993        cm.replaceSelection("");
1994    }
1995  }
1996
1997  function onKeyPress(e) {
1998    var cm = this;
1999    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2000    var keyCode = e.keyCode, charCode = e.charCode;
2001    if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2002    if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2003    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2004    if (this.options.electricChars && this.doc.mode.electricChars &&
2005        this.options.smartIndent && !isReadOnly(this) &&
2006        this.doc.mode.electricChars.indexOf(ch) > -1)
2007      setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2008    if (handleCharBinding(cm, e, ch)) return;
2009    fastPoll(cm);
2010  }
2011
2012  function onFocus(cm) {
2013    if (cm.options.readOnly == "nocursor") return;
2014    if (!cm.state.focused) {
2015      signal(cm, "focus", cm);
2016      cm.state.focused = true;
2017      if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2018        cm.display.wrapper.className += " CodeMirror-focused";
2019      resetInput(cm, true);
2020    }
2021    slowPoll(cm);
2022    restartBlink(cm);
2023  }
2024  function onBlur(cm) {
2025    if (cm.state.focused) {
2026      signal(cm, "blur", cm);
2027      cm.state.focused = false;
2028      cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2029    }
2030    clearInterval(cm.display.blinker);
2031    setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2032  }
2033
2034  var detectingSelectAll;
2035  function onContextMenu(cm, e) {
2036    var display = cm.display, sel = cm.doc.sel;
2037    if (eventInWidget(display, e)) return;
2038
2039    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2040    if (!pos || opera) return; // Opera is difficult.
2041    if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2042      operation(cm, setSelection)(cm.doc, pos, pos);
2043
2044    var oldCSS = display.input.style.cssText;
2045    display.inputDiv.style.position = "absolute";
2046    display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2047      "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2048      "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2049    focusInput(cm);
2050    resetInput(cm, true);
2051    // Adds "Select all" to context menu in FF
2052    if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2053
2054    function rehide() {
2055      display.inputDiv.style.position = "relative";
2056      display.input.style.cssText = oldCSS;
2057      if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2058      slowPoll(cm);
2059
2060      // Try to detect the user choosing select-all
2061      if (display.input.selectionStart != null && (!ie || ie_lt9)) {
2062        clearTimeout(detectingSelectAll);
2063        var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
2064        display.prevInput = " ";
2065        display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2066        var poll = function(){
2067          if (display.prevInput == " " && display.input.selectionStart == 0)
2068            operation(cm, commands.selectAll)(cm);
2069          else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2070          else resetInput(cm);
2071        };
2072        detectingSelectAll = setTimeout(poll, 200);
2073      }
2074    }
2075
2076    if (captureMiddleClick) {
2077      e_stop(e);
2078      var mouseup = function() {
2079        off(window, "mouseup", mouseup);
2080        setTimeout(rehide, 20);
2081      };
2082      on(window, "mouseup", mouseup);
2083    } else {
2084      setTimeout(rehide, 50);
2085    }
2086  }
2087
2088  // UPDATING
2089
2090  function changeEnd(change) {
2091    return Pos(change.from.line + change.text.length - 1,
2092               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2093  }
2094
2095  // Make sure a position will be valid after the given change.
2096  function clipPostChange(doc, change, pos) {
2097    if (!posLess(change.from, pos)) return clipPos(doc, pos);
2098    var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2099    if (pos.line > change.to.line + diff) {
2100      var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2101      if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2102      return clipToLen(pos, getLine(doc, preLine).text.length);
2103    }
2104    if (pos.line == change.to.line + diff)
2105      return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2106                       getLine(doc, change.to.line).text.length - change.to.ch);
2107    var inside = pos.line - change.from.line;
2108    return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2109  }
2110
2111  // Hint can be null|"end"|"start"|"around"|{anchor,head}
2112  function computeSelAfterChange(doc, change, hint) {
2113    if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2114      return {anchor: clipPostChange(doc, change, hint.anchor),
2115              head: clipPostChange(doc, change, hint.head)};
2116
2117    if (hint == "start") return {anchor: change.from, head: change.from};
2118
2119    var end = changeEnd(change);
2120    if (hint == "around") return {anchor: change.from, head: end};
2121    if (hint == "end") return {anchor: end, head: end};
2122
2123    // hint is null, leave the selection alone as much as possible
2124    var adjustPos = function(pos) {
2125      if (posLess(pos, change.from)) return pos;
2126      if (!posLess(change.to, pos)) return end;
2127
2128      var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2129      if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2130      return Pos(line, ch);
2131    };
2132    return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2133  }
2134
2135  function filterChange(doc, change) {
2136    var obj = {
2137      canceled: false,
2138      from: change.from,
2139      to: change.to,
2140      text: change.text,
2141      origin: change.origin,
2142      update: function(from, to, text, origin) {
2143        if (from) this.from = clipPos(doc, from);
2144        if (to) this.to = clipPos(doc, to);
2145        if (text) this.text = text;
2146        if (origin !== undefined) this.origin = origin;
2147      },
2148      cancel: function() { this.canceled = true; }
2149    };
2150    signal(doc, "beforeChange", doc, obj);
2151    if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2152
2153    if (obj.canceled) return null;
2154    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2155  }
2156
2157  // Replace the range from from to to by the strings in replacement.
2158  // change is a {from, to, text [, origin]} object
2159  function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2160    if (doc.cm) {
2161      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2162      if (doc.cm.state.suppressEdits) return;
2163    }
2164
2165    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2166      change = filterChange(doc, change);
2167      if (!change) return;
2168    }
2169
2170    // Possibly split or suppress the update based on the presence
2171    // of read-only spans in its range.
2172    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2173    if (split) {
2174      for (var i = split.length - 1; i >= 1; --i)
2175        makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2176      if (split.length)
2177        makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2178    } else {
2179      makeChangeNoReadonly(doc, change, selUpdate);
2180    }
2181  }
2182
2183  function makeChangeNoReadonly(doc, change, selUpdate) {
2184    var selAfter = computeSelAfterChange(doc, change, selUpdate);
2185    addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2186
2187    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2188    var rebased = [];
2189
2190    linkedDocs(doc, function(doc, sharedHist) {
2191      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2192        rebaseHist(doc.history, change);
2193        rebased.push(doc.history);
2194      }
2195      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2196    });
2197  }
2198
2199  function makeChangeFromHistory(doc, type) {
2200    var hist = doc.history;
2201    var event = (type == "undo" ? hist.done : hist.undone).pop();
2202    if (!event) return;
2203    hist.dirtyCounter += type == "undo" ? -1 : 1;
2204
2205    var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2206                anchorAfter: event.anchorBefore, headAfter: event.headBefore};
2207    (type == "undo" ? hist.undone : hist.done).push(anti);
2208
2209    for (var i = event.changes.length - 1; i >= 0; --i) {
2210      var change = event.changes[i];
2211      change.origin = type;
2212      anti.changes.push(historyChangeFromChange(doc, change));
2213
2214      var after = i ? computeSelAfterChange(doc, change, null)
2215                    : {anchor: event.anchorBefore, head: event.headBefore};
2216      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2217      var rebased = [];
2218
2219      linkedDocs(doc, function(doc, sharedHist) {
2220        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2221          rebaseHist(doc.history, change);
2222          rebased.push(doc.history);
2223        }
2224        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2225      });
2226    }
2227  }
2228
2229  function shiftDoc(doc, distance) {
2230    function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2231    doc.first += distance;
2232    if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2233    doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2234    doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2235  }
2236
2237  function makeChangeSingleDoc(doc, change, selAfter, spans) {
2238    if (doc.cm && !doc.cm.curOp)
2239      return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2240
2241    if (change.to.line < doc.first) {
2242      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2243      return;
2244    }
2245    if (change.from.line > doc.lastLine()) return;
2246
2247    // Clip the change to the size of this doc
2248    if (change.from.line < doc.first) {
2249      var shift = change.text.length - 1 - (doc.first - change.from.line);
2250      shiftDoc(doc, shift);
2251      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2252                text: [lst(change.text)], origin: change.origin};
2253    }
2254    var last = doc.lastLine();
2255    if (change.to.line > last) {
2256      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2257                text: [change.text[0]], origin: change.origin};
2258    }
2259
2260    change.removed = getBetween(doc, change.from, change.to);
2261
2262    if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2263    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2264    else updateDoc(doc, change, spans, selAfter);
2265  }
2266
2267  function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2268    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2269
2270    var recomputeMaxLength = false, checkWidthStart = from.line;
2271    if (!cm.options.lineWrapping) {
2272      checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2273      doc.iter(checkWidthStart, to.line + 1, function(line) {
2274        if (line == display.maxLine) {
2275          recomputeMaxLength = true;
2276          return true;
2277        }
2278      });
2279    }
2280
2281    updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2282
2283    if (!cm.options.lineWrapping) {
2284      doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2285        var len = lineLength(doc, line);
2286        if (len > display.maxLineLength) {
2287          display.maxLine = line;
2288          display.maxLineLength = len;
2289          display.maxLineChanged = true;
2290          recomputeMaxLength = false;
2291        }
2292      });
2293      if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2294    }
2295
2296    // Adjust frontier, schedule worker
2297    doc.frontier = Math.min(doc.frontier, from.line);
2298    startWorker(cm, 400);
2299
2300    var lendiff = change.text.length - (to.line - from.line) - 1;
2301    // Remember that these lines changed, for updating the display
2302    regChange(cm, from.line, to.line + 1, lendiff);
2303
2304    if (hasHandler(cm, "change")) {
2305      var changeObj = {from: from, to: to,
2306                       text: change.text,
2307                       removed: change.removed,
2308                       origin: change.origin};
2309      if (cm.curOp.textChanged) {
2310        for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2311        cur.next = changeObj;
2312      } else cm.curOp.textChanged = changeObj;
2313    }
2314  }
2315
2316  function replaceRange(doc, code, from, to, origin) {
2317    if (!to) to = from;
2318    if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2319    if (typeof code == "string") code = splitLines(code);
2320    makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2321  }
2322
2323  // POSITION OBJECT
2324
2325  function Pos(line, ch) {
2326    if (!(this instanceof Pos)) return new Pos(line, ch);
2327    this.line = line; this.ch = ch;
2328  }
2329  CodeMirror.Pos = Pos;
2330
2331  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2332  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2333  function copyPos(x) {return Pos(x.line, x.ch);}
2334
2335  // SELECTION
2336
2337  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2338  function clipPos(doc, pos) {
2339    if (pos.line < doc.first) return Pos(doc.first, 0);
2340    var last = doc.first + doc.size - 1;
2341    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2342    return clipToLen(pos, getLine(doc, pos.line).text.length);
2343  }
2344  function clipToLen(pos, linelen) {
2345    var ch = pos.ch;
2346    if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2347    else if (ch < 0) return Pos(pos.line, 0);
2348    else return pos;
2349  }
2350  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2351
2352  // If shift is held, this will move the selection anchor. Otherwise,
2353  // it'll set the whole selection.
2354  function extendSelection(doc, pos, other, bias) {
2355    if (doc.sel.shift || doc.sel.extend) {
2356      var anchor = doc.sel.anchor;
2357      if (other) {
2358        var posBefore = posLess(pos, anchor);
2359        if (posBefore != posLess(other, anchor)) {
2360          anchor = pos;
2361          pos = other;
2362        } else if (posBefore != posLess(pos, other)) {
2363          pos = other;
2364        }
2365      }
2366      setSelection(doc, anchor, pos, bias);
2367    } else {
2368      setSelection(doc, pos, other || pos, bias);
2369    }
2370    if (doc.cm) doc.cm.curOp.userSelChange = true;
2371  }
2372
2373  function filterSelectionChange(doc, anchor, head) {
2374    var obj = {anchor: anchor, head: head};
2375    signal(doc, "beforeSelectionChange", doc, obj);
2376    if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2377    obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2378    return obj;
2379  }
2380
2381  // Update the selection. Last two args are only used by
2382  // updateDoc, since they have to be expressed in the line
2383  // numbers before the update.
2384  function setSelection(doc, anchor, head, bias, checkAtomic) {
2385    if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2386      var filtered = filterSelectionChange(doc, anchor, head);
2387      head = filtered.head;
2388      anchor = filtered.anchor;
2389    }
2390
2391    var sel = doc.sel;
2392    sel.goalColumn = null;
2393    // Skip over atomic spans.
2394    if (checkAtomic || !posEq(anchor, sel.anchor))
2395      anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2396    if (checkAtomic || !posEq(head, sel.head))
2397      head = skipAtomic(doc, head, bias, checkAtomic != "push");
2398
2399    if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2400
2401    sel.anchor = anchor; sel.head = head;
2402    var inv = posLess(head, anchor);
2403    sel.from = inv ? head : anchor;
2404    sel.to = inv ? anchor : head;
2405
2406    if (doc.cm)
2407      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2408
2409    signalLater(doc, "cursorActivity", doc);
2410  }
2411
2412  function reCheckSelection(cm) {
2413    setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2414  }
2415
2416  function skipAtomic(doc, pos, bias, mayClear) {
2417    var flipped = false, curPos = pos;
2418    var dir = bias || 1;
2419    doc.cantEdit = false;
2420    search: for (;;) {
2421      var line = getLine(doc, curPos.line), toClear;
2422      if (line.markedSpans) {
2423        for (var i = 0; i < line.markedSpans.length; ++i) {
2424          var sp = line.markedSpans[i], m = sp.marker;
2425          if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2426              (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2427            if (mayClear && m.clearOnEnter) {
2428              (toClear || (toClear = [])).push(m);
2429              continue;
2430            } else if (!m.atomic) continue;
2431            var newPos = m.find()[dir < 0 ? "from" : "to"];
2432            if (posEq(newPos, curPos)) {
2433              newPos.ch += dir;
2434              if (newPos.ch < 0) {
2435                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2436                else newPos = null;
2437              } else if (newPos.ch > line.text.length) {
2438                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2439                else newPos = null;
2440              }
2441              if (!newPos) {
2442                if (flipped) {
2443                  // Driven in a corner -- no valid cursor position found at all
2444                  // -- try again *with* clearing, if we didn't already
2445                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
2446                  // Otherwise, turn off editing until further notice, and return the start of the doc
2447                  doc.cantEdit = true;
2448                  return Pos(doc.first, 0);
2449                }
2450                flipped = true; newPos = pos; dir = -dir;
2451              }
2452            }
2453            curPos = newPos;
2454            continue search;
2455          }
2456        }
2457        if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
2458      }
2459      return curPos;
2460    }
2461  }
2462
2463  // SCROLLING
2464
2465  function scrollCursorIntoView(cm) {
2466    var coords = scrollPosIntoView(cm, cm.doc.sel.head);
2467    if (!cm.state.focused) return;
2468    var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
2469    if (coords.top + pTop + box.top < 0) doScroll = true;
2470    else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2471    if (doScroll != null && !phantom) {
2472      var hidden = display.cursor.style.display == "none";
2473      if (hidden) {
2474        display.cursor.style.display = "";
2475        display.cursor.style.left = coords.left + "px";
2476        display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2477      }
2478      display.cursor.scrollIntoView(doScroll);
2479      if (hidden) display.cursor.style.display = "none";
2480    }
2481  }
2482
2483  function scrollPosIntoView(cm, pos, margin) {
2484    if (margin == null) margin = 0;
2485    for (;;) {
2486      var changed = false, coords = cursorCoords(cm, pos);
2487      var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2488      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2489      if (scrollPos.scrollTop != null) {
2490        setScrollTop(cm, scrollPos.scrollTop);
2491        if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2492      }
2493      if (scrollPos.scrollLeft != null) {
2494        setScrollLeft(cm, scrollPos.scrollLeft);
2495        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2496      }
2497      if (!changed) return coords;
2498    }
2499  }
2500
2501  function scrollIntoView(cm, x1, y1, x2, y2) {
2502    var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2503    if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2504    if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2505  }
2506
2507  function calculateScrollPos(cm, x1, y1, x2, y2) {
2508    var display = cm.display, pt = paddingTop(display);
2509    y1 += pt; y2 += pt;
2510    var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2511    var docBottom = cm.doc.height + paddingVert(display);
2512    var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2513    if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2514    else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
2515
2516    var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2517    x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2518    var gutterw = display.gutters.offsetWidth;
2519    var atLeft = x1 < gutterw + 10;
2520    if (x1 < screenleft + gutterw || atLeft) {
2521      if (atLeft) x1 = 0;
2522      result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2523    } else if (x2 > screenw + screenleft - 3) {
2524      result.scrollLeft = x2 + 10 - screenw;
2525    }
2526    return result;
2527  }
2528
2529  function updateScrollPos(cm, left, top) {
2530    cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top};
2531  }
2532
2533  function addToScrollPos(cm, left, top) {
2534    var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2535    var scroll = cm.display.scroller;
2536    pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2537    pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2538  }
2539
2540  // API UTILITIES
2541
2542  function indentLine(cm, n, how, aggressive) {
2543    var doc = cm.doc;
2544    if (!how) how = "add";
2545    if (how == "smart") {
2546      if (!cm.doc.mode.indent) how = "prev";
2547      else var state = getStateBefore(cm, n);
2548    }
2549
2550    var tabSize = cm.options.tabSize;
2551    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2552    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2553    if (how == "smart") {
2554      indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2555      if (indentation == Pass) {
2556        if (!aggressive) return;
2557        how = "prev";
2558      }
2559    }
2560    if (how == "prev") {
2561      if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2562      else indentation = 0;
2563    } else if (how == "add") {
2564      indentation = curSpace + cm.options.indentUnit;
2565    } else if (how == "subtract") {
2566      indentation = curSpace - cm.options.indentUnit;
2567    }
2568    indentation = Math.max(0, indentation);
2569
2570    var indentString = "", pos = 0;
2571    if (cm.options.indentWithTabs)
2572      for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2573    if (pos < indentation) indentString += spaceStr(indentation - pos);
2574
2575    if (indentString != curSpaceString)
2576      replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2577    line.stateAfter = null;
2578  }
2579
2580  function changeLine(cm, handle, op) {
2581    var no = handle, line = handle, doc = cm.doc;
2582    if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2583    else no = lineNo(handle);
2584    if (no == null) return null;
2585    if (op(line, no)) regChange(cm, no, no + 1);
2586    else return null;
2587    return line;
2588  }
2589
2590  function findPosH(doc, pos, dir, unit, visually) {
2591    var line = pos.line, ch = pos.ch;
2592    var lineObj = getLine(doc, line);
2593    var possible = true;
2594    function findNextLine() {
2595      var l = line + dir;
2596      if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2597      line = l;
2598      return lineObj = getLine(doc, l);
2599    }
2600    function moveOnce(boundToLine) {
2601      var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2602      if (next == null) {
2603        if (!boundToLine && findNextLine()) {
2604          if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2605          else ch = dir < 0 ? lineObj.text.length : 0;
2606        } else return (possible = false);
2607      } else ch = next;
2608      return true;
2609    }
2610
2611    if (unit == "char") moveOnce();
2612    else if (unit == "column") moveOnce(true);
2613    else if (unit == "word" || unit == "group") {
2614      var sawType = null, group = unit == "group";
2615      for (var first = true;; first = false) {
2616        if (dir < 0 && !moveOnce(!first)) break;
2617        var cur = lineObj.text.charAt(ch) || "\n";
2618        var type = isWordChar(cur) ? "w"
2619          : !group ? null
2620          : /\s/.test(cur) ? null
2621          : "p";
2622        if (sawType && sawType != type) {
2623          if (dir < 0) {dir = 1; moveOnce();}
2624          break;
2625        }
2626        if (type) sawType = type;
2627        if (dir > 0 && !moveOnce(!first)) break;
2628      }
2629    }
2630    var result = skipAtomic(doc, Pos(line, ch), dir, true);
2631    if (!possible) result.hitSide = true;
2632    return result;
2633  }
2634
2635  function findPosV(cm, pos, dir, unit) {
2636    var doc = cm.doc, x = pos.left, y;
2637    if (unit == "page") {
2638      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2639      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2640    } else if (unit == "line") {
2641      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2642    }
2643    for (;;) {
2644      var target = coordsChar(cm, x, y);
2645      if (!target.outside) break;
2646      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2647      y += dir * 5;
2648    }
2649    return target;
2650  }
2651
2652  function findWordAt(line, pos) {
2653    var start = pos.ch, end = pos.ch;
2654    if (line) {
2655      if (pos.after === false || end == line.length) --start; else ++end;
2656      var startChar = line.charAt(start);
2657      var check = isWordChar(startChar) ? isWordChar
2658        : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2659        : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2660      while (start > 0 && check(line.charAt(start - 1))) --start;
2661      while (end < line.length && check(line.charAt(end))) ++end;
2662    }
2663    return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2664  }
2665
2666  function selectLine(cm, line) {
2667    extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2668  }
2669
2670  // PROTOTYPE
2671
2672  // The publicly visible API. Note that operation(null, f) means
2673  // 'wrap f in an operation, performed on its `this` parameter'
2674
2675  CodeMirror.prototype = {
2676    focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2677
2678    setOption: function(option, value) {
2679      var options = this.options, old = options[option];
2680      if (options[option] == value && option != "mode") return;
2681      options[option] = value;
2682      if (optionHandlers.hasOwnProperty(option))
2683        operation(this, optionHandlers[option])(this, value, old);
2684    },
2685
2686    getOption: function(option) {return this.options[option];},
2687    getDoc: function() {return this.doc;},
2688
2689    addKeyMap: function(map) {
2690      this.state.keyMaps.push(map);
2691    },
2692    removeKeyMap: function(map) {
2693      var maps = this.state.keyMaps;
2694      for (var i = 0; i < maps.length; ++i)
2695        if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2696          maps.splice(i, 1);
2697          return true;
2698        }
2699    },
2700
2701    addOverlay: operation(null, function(spec, options) {
2702      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2703      if (mode.startState) throw new Error("Overlays may not be stateful.");
2704      this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2705      this.state.modeGen++;
2706      regChange(this);
2707    }),
2708    removeOverlay: operation(null, function(spec) {
2709      var overlays = this.state.overlays;
2710      for (var i = 0; i < overlays.length; ++i) {
2711        if (overlays[i].modeSpec == spec) {
2712          overlays.splice(i, 1);
2713          this.state.modeGen++;
2714          regChange(this);
2715          return;
2716        }
2717      }
2718    }),
2719
2720    indentLine: operation(null, function(n, dir, aggressive) {
2721      if (typeof dir != "string") {
2722        if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2723        else dir = dir ? "add" : "subtract";
2724      }
2725      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2726    }),
2727    indentSelection: operation(null, function(how) {
2728      var sel = this.doc.sel;
2729      if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2730      var e = sel.to.line - (sel.to.ch ? 0 : 1);
2731      for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2732    }),
2733
2734    // Fetch the parser token for a given character. Useful for hacks
2735    // that want to inspect the mode state (say, for completion).
2736    getTokenAt: function(pos) {
2737      var doc = this.doc;
2738      pos = clipPos(doc, pos);
2739      var state = getStateBefore(this, pos.line), mode = this.doc.mode;
2740      var line = getLine(doc, pos.line);
2741      var stream = new StringStream(line.text, this.options.tabSize);
2742      while (stream.pos < pos.ch && !stream.eol()) {
2743        stream.start = stream.pos;
2744        var style = mode.token(stream, state);
2745      }
2746      return {start: stream.start,
2747              end: stream.pos,
2748              string: stream.current(),
2749              className: style || null, // Deprecated, use 'type' instead
2750              type: style || null,
2751              state: state};
2752    },
2753
2754    getStateAfter: function(line) {
2755      var doc = this.doc;
2756      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2757      return getStateBefore(this, line + 1);
2758    },
2759
2760    cursorCoords: function(start, mode) {
2761      var pos, sel = this.doc.sel;
2762      if (start == null) pos = sel.head;
2763      else if (typeof start == "object") pos = clipPos(this.doc, start);
2764      else pos = start ? sel.from : sel.to;
2765      return cursorCoords(this, pos, mode || "page");
2766    },
2767
2768    charCoords: function(pos, mode) {
2769      return charCoords(this, clipPos(this.doc, pos), mode || "page");
2770    },
2771
2772    coordsChar: function(coords, mode) {
2773      coords = fromCoordSystem(this, coords, mode || "page");
2774      return coordsChar(this, coords.left, coords.top);
2775    },
2776
2777    defaultTextHeight: function() { return textHeight(this.display); },
2778    defaultCharWidth: function() { return charWidth(this.display); },
2779
2780    setGutterMarker: operation(null, function(line, gutterID, value) {
2781      return changeLine(this, line, function(line) {
2782        var markers = line.gutterMarkers || (line.gutterMarkers = {});
2783        markers[gutterID] = value;
2784        if (!value && isEmpty(markers)) line.gutterMarkers = null;
2785        return true;
2786      });
2787    }),
2788
2789    clearGutter: operation(null, function(gutterID) {
2790      var cm = this, doc = cm.doc, i = doc.first;
2791      doc.iter(function(line) {
2792        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2793          line.gutterMarkers[gutterID] = null;
2794          regChange(cm, i, i + 1);
2795          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2796        }
2797        ++i;
2798      });
2799    }),
2800
2801    addLineClass: operation(null, function(handle, where, cls) {
2802      return changeLine(this, handle, function(line) {
2803        var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2804        if (!line[prop]) line[prop] = cls;
2805        else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2806        else line[prop] += " " + cls;
2807        return true;
2808      });
2809    }),
2810
2811    removeLineClass: operation(null, function(handle, where, cls) {
2812      return changeLine(this, handle, function(line) {
2813        var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2814        var cur = line[prop];
2815        if (!cur) return false;
2816        else if (cls == null) line[prop] = null;
2817        else {
2818          var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2819          if (upd == cur) return false;
2820          line[prop] = upd || null;
2821        }
2822        return true;
2823      });
2824    }),
2825
2826    addLineWidget: operation(null, function(handle, node, options) {
2827      return addLineWidget(this, handle, node, options);
2828    }),
2829
2830    removeLineWidget: function(widget) { widget.clear(); },
2831
2832    lineInfo: function(line) {
2833      if (typeof line == "number") {
2834        if (!isLine(this.doc, line)) return null;
2835        var n = line;
2836        line = getLine(this.doc, line);
2837        if (!line) return null;
2838      } else {
2839        var n = lineNo(line);
2840        if (n == null) return null;
2841      }
2842      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2843              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2844              widgets: line.widgets};
2845    },
2846
2847    getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2848
2849    addWidget: function(pos, node, scroll, vert, horiz) {
2850      var display = this.display;
2851      pos = cursorCoords(this, clipPos(this.doc, pos));
2852      var top = pos.bottom, left = pos.left;
2853      node.style.position = "absolute";
2854      display.sizer.appendChild(node);
2855      if (vert == "over") {
2856        top = pos.top;
2857      } else if (vert == "above" || vert == "near") {
2858        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
2859        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2860        // Default to positioning above (if specified and possible); otherwise default to positioning below
2861        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
2862          top = pos.top - node.offsetHeight;
2863        else if (pos.bottom + node.offsetHeight <= vspace)
2864          top = pos.bottom;
2865        if (left + node.offsetWidth > hspace)
2866          left = hspace - node.offsetWidth;
2867      }
2868      node.style.top = (top + paddingTop(display)) + "px";
2869      node.style.left = node.style.right = "";
2870      if (horiz == "right") {
2871        left = display.sizer.clientWidth - node.offsetWidth;
2872        node.style.right = "0px";
2873      } else {
2874        if (horiz == "left") left = 0;
2875        else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
2876        node.style.left = left + "px";
2877      }
2878      if (scroll)
2879        scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2880    },
2881
2882    triggerOnKeyDown: operation(null, onKeyDown),
2883
2884    execCommand: function(cmd) {return commands[cmd](this);},
2885
2886    findPosH: function(from, amount, unit, visually) {
2887      var dir = 1;
2888      if (amount < 0) { dir = -1; amount = -amount; }
2889      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2890        cur = findPosH(this.doc, cur, dir, unit, visually);
2891        if (cur.hitSide) break;
2892      }
2893      return cur;
2894    },
2895
2896    moveH: operation(null, function(dir, unit) {
2897      var sel = this.doc.sel, pos;
2898      if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2899        pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
2900      else
2901        pos = dir < 0 ? sel.from : sel.to;
2902      extendSelection(this.doc, pos, pos, dir);
2903    }),
2904
2905    deleteH: operation(null, function(dir, unit) {
2906      var sel = this.doc.sel;
2907      if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
2908      else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
2909      this.curOp.userSelChange = true;
2910    }),
2911
2912    findPosV: function(from, amount, unit, goalColumn) {
2913      var dir = 1, x = goalColumn;
2914      if (amount < 0) { dir = -1; amount = -amount; }
2915      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2916        var coords = cursorCoords(this, cur, "div");
2917        if (x == null) x = coords.left;
2918        else coords.left = x;
2919        cur = findPosV(this, coords, dir, unit);
2920        if (cur.hitSide) break;
2921      }
2922      return cur;
2923    },
2924
2925    moveV: operation(null, function(dir, unit) {
2926      var sel = this.doc.sel;
2927      var pos = cursorCoords(this, sel.head, "div");
2928      if (sel.goalColumn != null) pos.left = sel.goalColumn;
2929      var target = findPosV(this, pos, dir, unit);
2930
2931      if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
2932      extendSelection(this.doc, target, target, dir);
2933      sel.goalColumn = pos.left;
2934    }),
2935
2936    toggleOverwrite: function() {
2937      if (this.state.overwrite = !this.state.overwrite)
2938        this.display.cursor.className += " CodeMirror-overwrite";
2939      else
2940        this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2941    },
2942    hasFocus: function() { return this.state.focused; },
2943
2944    scrollTo: operation(null, function(x, y) {
2945      updateScrollPos(this, x, y);
2946    }),
2947    getScrollInfo: function() {
2948      var scroller = this.display.scroller, co = scrollerCutOff;
2949      return {left: scroller.scrollLeft, top: scroller.scrollTop,
2950              height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
2951              clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2952    },
2953
2954    scrollIntoView: function(pos, margin) {
2955      if (typeof pos == "number") pos = Pos(pos, 0);
2956      if (!pos || pos.line != null) {
2957        pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
2958        scrollPosIntoView(this, pos, margin);
2959      } else {
2960        scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
2961      }
2962    },
2963
2964    setSize: function(width, height) {
2965      function interpret(val) {
2966        return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
2967      }
2968      if (width != null) this.display.wrapper.style.width = interpret(width);
2969      if (height != null) this.display.wrapper.style.height = interpret(height);
2970      this.refresh();
2971    },
2972
2973    on: function(type, f) {on(this, type, f);},
2974    off: function(type, f) {off(this, type, f);},
2975
2976    operation: function(f){return runInOp(this, f);},
2977
2978    refresh: operation(null, function() {
2979      clearCaches(this);
2980      updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
2981      regChange(this);
2982    }),
2983
2984    swapDoc: operation(null, function(doc) {
2985      var old = this.doc;
2986      old.cm = null;
2987      attachDoc(this, doc);
2988      clearCaches(this);
2989      updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
2990      return old;
2991    }),
2992
2993    getInputField: function(){return this.display.input;},
2994    getWrapperElement: function(){return this.display.wrapper;},
2995    getScrollerElement: function(){return this.display.scroller;},
2996    getGutterElement: function(){return this.display.gutters;}
2997  };
2998
2999  // OPTION DEFAULTS
3000
3001  var optionHandlers = CodeMirror.optionHandlers = {};
3002
3003  // The default configuration options.
3004  var defaults = CodeMirror.defaults = {};
3005
3006  function option(name, deflt, handle, notOnInit) {
3007    CodeMirror.defaults[name] = deflt;
3008    if (handle) optionHandlers[name] =
3009      notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3010  }
3011
3012  var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3013
3014  // These two are, on init, called from the constructor because they
3015  // have to be initialized before the editor can start at all.
3016  option("value", "", function(cm, val) {
3017    cm.setValue(val);
3018  }, true);
3019  option("mode", null, function(cm, val) {
3020    cm.doc.modeOption = val;
3021    loadMode(cm);
3022  }, true);
3023
3024  option("indentUnit", 2, loadMode, true);
3025  option("indentWithTabs", false);
3026  option("smartIndent", true);
3027  option("tabSize", 4, function(cm) {
3028    loadMode(cm);
3029    clearCaches(cm);
3030    regChange(cm);
3031  }, true);
3032  option("electricChars", true);
3033  option("rtlMoveVisually", !windows);
3034
3035  option("theme", "default", function(cm) {
3036    themeChanged(cm);
3037    guttersChanged(cm);
3038  }, true);
3039  option("keyMap", "default", keyMapChanged);
3040  option("extraKeys", null);
3041
3042  option("onKeyEvent", null);
3043  option("onDragEvent", null);
3044
3045  option("lineWrapping", false, wrappingChanged, true);
3046  option("gutters", [], function(cm) {
3047    setGuttersForLineNumbers(cm.options);
3048    guttersChanged(cm);
3049  }, true);
3050  option("fixedGutter", true, function(cm, val) {
3051    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3052    cm.refresh();
3053  }, true);
3054  option("lineNumbers", false, function(cm) {
3055    setGuttersForLineNumbers(cm.options);
3056    guttersChanged(cm);
3057  }, true);
3058  option("firstLineNumber", 1, guttersChanged, true);
3059  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3060  option("showCursorWhenSelecting", false, updateSelection, true);
3061
3062  option("readOnly", false, function(cm, val) {
3063    if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3064    else if (!val) resetInput(cm, true);
3065  });
3066  option("dragDrop", true);
3067
3068  option("cursorBlinkRate", 530);
3069  option("cursorHeight", 1);
3070  option("workTime", 100);
3071  option("workDelay", 100);
3072  option("flattenSpans", true);
3073  option("pollInterval", 100);
3074  option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3075  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3076
3077  option("tabindex", null, function(cm, val) {
3078    cm.display.input.tabIndex = val || "";
3079  });
3080  option("autofocus", null);
3081
3082  // MODE DEFINITION AND QUERYING
3083
3084  // Known modes, by name and by MIME
3085  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3086
3087  CodeMirror.defineMode = function(name, mode) {
3088    if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3089    if (arguments.length > 2) {
3090      mode.dependencies = [];
3091      for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3092    }
3093    modes[name] = mode;
3094  };
3095
3096  CodeMirror.defineMIME = function(mime, spec) {
3097    mimeModes[mime] = spec;
3098  };
3099
3100  CodeMirror.resolveMode = function(spec) {
3101    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
3102      spec = mimeModes[spec];
3103    else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
3104      return CodeMirror.resolveMode("application/xml");
3105    if (typeof spec == "string") return {name: spec};
3106    else return spec || {name: "null"};
3107  };
3108
3109  CodeMirror.getMode = function(options, spec) {
3110    spec = CodeMirror.resolveMode(spec);
3111    var mfactory = modes[spec.name];
3112    if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3113    var modeObj = mfactory(options, spec);
3114    if (modeExtensions.hasOwnProperty(spec.name)) {
3115      var exts = modeExtensions[spec.name];
3116      for (var prop in exts) {
3117        if (!exts.hasOwnProperty(prop)) continue;
3118        if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3119        modeObj[prop] = exts[prop];
3120      }
3121    }
3122    modeObj.name = spec.name;
3123    return modeObj;
3124  };
3125
3126  CodeMirror.defineMode("null", function() {
3127    return {token: function(stream) {stream.skipToEnd();}};
3128  });
3129  CodeMirror.defineMIME("text/plain", "null");
3130
3131  var modeExtensions = CodeMirror.modeExtensions = {};
3132  CodeMirror.extendMode = function(mode, properties) {
3133    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3134    copyObj(properties, exts);
3135  };
3136
3137  // EXTENSIONS
3138
3139  CodeMirror.defineExtension = function(name, func) {
3140    CodeMirror.prototype[name] = func;
3141  };
3142
3143  CodeMirror.defineOption = option;
3144
3145  var initHooks = [];
3146  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3147
3148  // MODE STATE HANDLING
3149
3150  // Utility functions for working with state. Exported because modes
3151  // sometimes need to do this.
3152  function copyState(mode, state) {
3153    if (state === true) return state;
3154    if (mode.copyState) return mode.copyState(state);
3155    var nstate = {};
3156    for (var n in state) {
3157      var val = state[n];
3158      if (val instanceof Array) val = val.concat([]);
3159      nstate[n] = val;
3160    }
3161    return nstate;
3162  }
3163  CodeMirror.copyState = copyState;
3164
3165  function startState(mode, a1, a2) {
3166    return mode.startState ? mode.startState(a1, a2) : true;
3167  }
3168  CodeMirror.startState = startState;
3169
3170  CodeMirror.innerMode = function(mode, state) {
3171    while (mode.innerMode) {
3172      var info = mode.innerMode(state);
3173      state = info.state;
3174      mode = info.mode;
3175    }
3176    return info || {mode: mode, state: state};
3177  };
3178
3179  // STANDARD COMMANDS
3180
3181  var commands = CodeMirror.commands = {
3182    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3183    killLine: function(cm) {
3184      var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3185      if (!sel && cm.getLine(from.line).length == from.ch)
3186        cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3187      else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3188    },
3189    deleteLine: function(cm) {
3190      var l = cm.getCursor().line;
3191      cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3192    },
3193    undo: function(cm) {cm.undo();},
3194    redo: function(cm) {cm.redo();},
3195    goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3196    goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3197    goLineStart: function(cm) {
3198      cm.extendSelection(lineStart(cm, cm.getCursor().line));
3199    },
3200    goLineStartSmart: function(cm) {
3201      var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3202      var line = cm.getLineHandle(start.line);
3203      var order = getOrder(line);
3204      if (!order || order[0].level == 0) {
3205        var firstNonWS = Math.max(0, line.text.search(/\S/));
3206        var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3207        cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3208      } else cm.extendSelection(start);
3209    },
3210    goLineEnd: function(cm) {
3211      cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3212    },
3213    goLineRight: function(cm) {
3214      var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3215      cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3216    },
3217    goLineLeft: function(cm) {
3218      var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3219      cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3220    },
3221    goLineUp: function(cm) {cm.moveV(-1, "line");},
3222    goLineDown: function(cm) {cm.moveV(1, "line");},
3223    goPageUp: function(cm) {cm.moveV(-1, "page");},
3224    goPageDown: function(cm) {cm.moveV(1, "page");},
3225    goCharLeft: function(cm) {cm.moveH(-1, "char");},
3226    goCharRight: function(cm) {cm.moveH(1, "char");},
3227    goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3228    goColumnRight: function(cm) {cm.moveH(1, "column");},
3229    goWordLeft: function(cm) {cm.moveH(-1, "word");},
3230    goGroupRight: function(cm) {cm.moveH(1, "group");},
3231    goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3232    goWordRight: function(cm) {cm.moveH(1, "word");},
3233    delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3234    delCharAfter: function(cm) {cm.deleteH(1, "char");},
3235    delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3236    delWordAfter: function(cm) {cm.deleteH(1, "word");},
3237    delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3238    delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3239    indentAuto: function(cm) {cm.indentSelection("smart");},
3240    indentMore: function(cm) {cm.indentSelection("add");},
3241    indentLess: function(cm) {cm.indentSelection("subtract");},
3242    insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3243    defaultTab: function(cm) {
3244      if (cm.somethingSelected()) cm.indentSelection("add");
3245      else cm.replaceSelection("\t", "end", "+input");
3246    },
3247    transposeChars: function(cm) {
3248      var cur = cm.getCursor(), line = cm.getLine(cur.line);
3249      if (cur.ch > 0 && cur.ch < line.length - 1)
3250        cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3251                        Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3252    },
3253    newlineAndIndent: function(cm) {
3254      operation(cm, function() {
3255        cm.replaceSelection("\n", "end", "+input");
3256        cm.indentLine(cm.getCursor().line, null, true);
3257      })();
3258    },
3259    toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3260  };
3261
3262  // STANDARD KEYMAPS
3263
3264  var keyMap = CodeMirror.keyMap = {};
3265  keyMap.basic = {
3266    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3267    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3268    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3269    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3270  };
3271  // Note that the save and find-related commands aren't defined by
3272  // default. Unknown commands are simply ignored.
3273  keyMap.pcDefault = {
3274    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3275    "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3276    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3277    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3278    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3279    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3280    fallthrough: "basic"
3281  };
3282  keyMap.macDefault = {
3283    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3284    "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3285    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3286    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3287    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3288    "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3289    fallthrough: ["basic", "emacsy"]
3290  };
3291  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3292  keyMap.emacsy = {
3293    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3294    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3295    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3296    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3297  };
3298
3299  // KEYMAP DISPATCH
3300
3301  function getKeyMap(val) {
3302    if (typeof val == "string") return keyMap[val];
3303    else return val;
3304  }
3305
3306  function lookupKey(name, maps, handle) {
3307    function lookup(map) {
3308      map = getKeyMap(map);
3309      var found = map[name];
3310      if (found === false) return "stop";
3311      if (found != null && handle(found)) return true;
3312      if (map.nofallthrough) return "stop";
3313
3314      var fallthrough = map.fallthrough;
3315      if (fallthrough == null) return false;
3316      if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3317        return lookup(fallthrough);
3318      for (var i = 0, e = fallthrough.length; i < e; ++i) {
3319        var done = lookup(fallthrough[i]);
3320        if (done) return done;
3321      }
3322      return false;
3323    }
3324
3325    for (var i = 0; i < maps.length; ++i) {
3326      var done = lookup(maps[i]);
3327      if (done) return done;
3328    }
3329  }
3330  function isModifierKey(event) {
3331    var name = keyNames[event.keyCode];
3332    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3333  }
3334  function keyName(event, noShift) {
3335    var name = keyNames[event.keyCode];
3336    if (name == null || event.altGraphKey) return false;
3337    if (event.altKey) name = "Alt-" + name;
3338    if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3339    if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3340    if (!noShift && event.shiftKey) name = "Shift-" + name;
3341    return name;
3342  }
3343  CodeMirror.lookupKey = lookupKey;
3344  CodeMirror.isModifierKey = isModifierKey;
3345  CodeMirror.keyName = keyName;
3346
3347  // FROMTEXTAREA
3348
3349  CodeMirror.fromTextArea = function(textarea, options) {
3350    if (!options) options = {};
3351    options.value = textarea.value;
3352    if (!options.tabindex && textarea.tabindex)
3353      options.tabindex = textarea.tabindex;
3354    if (!options.placeholder && textarea.placeholder)
3355      options.placeholder = textarea.placeholder;
3356    // Set autofocus to true if this textarea is focused, or if it has
3357    // autofocus and no other element is focused.
3358    if (options.autofocus == null) {
3359      var hasFocus = document.body;
3360      // doc.activeElement occasionally throws on IE
3361      try { hasFocus = document.activeElement; } catch(e) {}
3362      options.autofocus = hasFocus == textarea ||
3363        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3364    }
3365
3366    function save() {textarea.value = cm.getValue();}
3367    if (textarea.form) {
3368      on(textarea.form, "submit", save);
3369      // Deplorable hack to make the submit method do the right thing.
3370      if (!options.leaveSubmitMethodAlone) {
3371        var form = textarea.form, realSubmit = form.submit;
3372        try {
3373          var wrappedSubmit = form.submit = function() {
3374            save();
3375            form.submit = realSubmit;
3376            form.submit();
3377            form.submit = wrappedSubmit;
3378          };
3379        } catch(e) {}
3380      }
3381    }
3382
3383    textarea.style.display = "none";
3384    var cm = CodeMirror(function(node) {
3385      textarea.parentNode.insertBefore(node, textarea.nextSibling);
3386    }, options);
3387    cm.save = save;
3388    cm.getTextArea = function() { return textarea; };
3389    cm.toTextArea = function() {
3390      save();
3391      textarea.parentNode.removeChild(cm.getWrapperElement());
3392      textarea.style.display = "";
3393      if (textarea.form) {
3394        off(textarea.form, "submit", save);
3395        if (typeof textarea.form.submit == "function")
3396          textarea.form.submit = realSubmit;
3397      }
3398    };
3399    return cm;
3400  };
3401
3402  // STRING STREAM
3403
3404  // Fed to the mode parsers, provides helper functions to make
3405  // parsers more succinct.
3406
3407  // The character stream used by a mode's parser.
3408  function StringStream(string, tabSize) {
3409    this.pos = this.start = 0;
3410    this.string = string;
3411    this.tabSize = tabSize || 8;
3412    this.lastColumnPos = this.lastColumnValue = 0;
3413  }
3414
3415  StringStream.prototype = {
3416    eol: function() {return this.pos >= this.string.length;},
3417    sol: function() {return this.pos == 0;},
3418    peek: function() {return this.string.charAt(this.pos) || undefined;},
3419    next: function() {
3420      if (this.pos < this.string.length)
3421        return this.string.charAt(this.pos++);
3422    },
3423    eat: function(match) {
3424      var ch = this.string.charAt(this.pos);
3425      if (typeof match == "string") var ok = ch == match;
3426      else var ok = ch && (match.test ? match.test(ch) : match(ch));
3427      if (ok) {++this.pos; return ch;}
3428    },
3429    eatWhile: function(match) {
3430      var start = this.pos;
3431      while (this.eat(match)){}
3432      return this.pos > start;
3433    },
3434    eatSpace: function() {
3435      var start = this.pos;
3436      while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3437      return this.pos > start;
3438    },
3439    skipToEnd: function() {this.pos = this.string.length;},
3440    skipTo: function(ch) {
3441      var found = this.string.indexOf(ch, this.pos);
3442      if (found > -1) {this.pos = found; return true;}
3443    },
3444    backUp: function(n) {this.pos -= n;},
3445    column: function() {
3446      if (this.lastColumnPos < this.start) {
3447        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3448        this.lastColumnPos = this.start;
3449      }
3450      return this.lastColumnValue;
3451    },
3452    indentation: function() {return countColumn(this.string, null, this.tabSize);},
3453    match: function(pattern, consume, caseInsensitive) {
3454      if (typeof pattern == "string") {
3455        var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3456        var substr = this.string.substr(this.pos, pattern.length);
3457        if (cased(substr) == cased(pattern)) {
3458          if (consume !== false) this.pos += pattern.length;
3459          return true;
3460        }
3461      } else {
3462        var match = this.string.slice(this.pos).match(pattern);
3463        if (match && match.index > 0) return null;
3464        if (match && consume !== false) this.pos += match[0].length;
3465        return match;
3466      }
3467    },
3468    current: function(){return this.string.slice(this.start, this.pos);}
3469  };
3470  CodeMirror.StringStream = StringStream;
3471
3472  // TEXTMARKERS
3473
3474  function TextMarker(doc, type) {
3475    this.lines = [];
3476    this.type = type;
3477    this.doc = doc;
3478  }
3479  CodeMirror.TextMarker = TextMarker;
3480
3481  TextMarker.prototype.clear = function() {
3482    if (this.explicitlyCleared) return;
3483    var cm = this.doc.cm, withOp = cm && !cm.curOp;
3484    if (withOp) startOperation(cm);
3485    var min = null, max = null;
3486    for (var i = 0; i < this.lines.length; ++i) {
3487      var line = this.lines[i];
3488      var span = getMarkedSpanFor(line.markedSpans, this);
3489      if (span.to != null) max = lineNo(line);
3490      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3491      if (span.from != null)
3492        min = lineNo(line);
3493      else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3494        updateLineHeight(line, textHeight(cm.display));
3495    }
3496    if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3497      var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3498      if (len > cm.display.maxLineLength) {
3499        cm.display.maxLine = visual;
3500        cm.display.maxLineLength = len;
3501        cm.display.maxLineChanged = true;
3502      }
3503    }
3504
3505    if (min != null && cm) regChange(cm, min, max + 1);
3506    this.lines.length = 0;
3507    this.explicitlyCleared = true;
3508    if (this.collapsed && this.doc.cantEdit) {
3509      this.doc.cantEdit = false;
3510      if (cm) reCheckSelection(cm);
3511    }
3512    if (withOp) endOperation(cm);
3513    signalLater(this, "clear");
3514  };
3515
3516  TextMarker.prototype.find = function() {
3517    var from, to;
3518    for (var i = 0; i < this.lines.length; ++i) {
3519      var line = this.lines[i];
3520      var span = getMarkedSpanFor(line.markedSpans, this);
3521      if (span.from != null || span.to != null) {
3522        var found = lineNo(line);
3523        if (span.from != null) from = Pos(found, span.from);
3524        if (span.to != null) to = Pos(found, span.to);
3525      }
3526    }
3527    if (this.type == "bookmark") return from;
3528    return from && {from: from, to: to};
3529  };
3530
3531  TextMarker.prototype.getOptions = function(copyWidget) {
3532    var repl = this.replacedWith;
3533    return {className: this.className,
3534            inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3535            atomic: this.atomic,
3536            collapsed: this.collapsed,
3537            clearOnEnter: this.clearOnEnter,
3538            replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3539            readOnly: this.readOnly,
3540            startStyle: this.startStyle, endStyle: this.endStyle};
3541  };
3542
3543  TextMarker.prototype.attachLine = function(line) {
3544    if (!this.lines.length && this.doc.cm) {
3545      var op = this.doc.cm.curOp;
3546      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3547        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3548    }
3549    this.lines.push(line);
3550  };
3551  TextMarker.prototype.detachLine = function(line) {
3552    this.lines.splice(indexOf(this.lines, line), 1);
3553    if (!this.lines.length && this.doc.cm) {
3554      var op = this.doc.cm.curOp;
3555      (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3556    }
3557  };
3558
3559  function markText(doc, from, to, options, type) {
3560    if (options && options.shared) return markTextShared(doc, from, to, options, type);
3561    if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3562
3563    var marker = new TextMarker(doc, type);
3564    if (type == "range" && !posLess(from, to)) return marker;
3565    if (options) copyObj(options, marker);
3566    if (marker.replacedWith) {
3567      marker.collapsed = true;
3568      marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3569    }
3570    if (marker.collapsed) sawCollapsedSpans = true;
3571
3572    var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3573    doc.iter(curLine, to.line + 1, function(line) {
3574      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3575        updateMaxLine = true;
3576      var span = {from: null, to: null, marker: marker};
3577      size += line.text.length;
3578      if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3579      if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3580      if (marker.collapsed) {
3581        if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3582        if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3583        else updateLineHeight(line, 0);
3584      }
3585      addMarkedSpan(line, span);
3586      ++curLine;
3587    });
3588    if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3589      if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3590    });
3591
3592    if (marker.readOnly) {
3593      sawReadOnlySpans = true;
3594      if (doc.history.done.length || doc.history.undone.length)
3595        doc.clearHistory();
3596    }
3597    if (marker.collapsed) {
3598      if (collapsedAtStart != collapsedAtEnd)
3599        throw new Error("Inserting collapsed marker overlapping an existing one");
3600      marker.size = size;
3601      marker.atomic = true;
3602    }
3603    if (cm) {
3604      if (updateMaxLine) cm.curOp.updateMaxLine = true;
3605      if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3606        regChange(cm, from.line, to.line + 1);
3607      if (marker.atomic) reCheckSelection(cm);
3608    }
3609    return marker;
3610  }
3611
3612  // SHARED TEXTMARKERS
3613
3614  function SharedTextMarker(markers, primary) {
3615    this.markers = markers;
3616    this.primary = primary;
3617    for (var i = 0, me = this; i < markers.length; ++i) {
3618      markers[i].parent = this;
3619      on(markers[i], "clear", function(){me.clear();});
3620    }
3621  }
3622  CodeMirror.SharedTextMarker = SharedTextMarker;
3623
3624  SharedTextMarker.prototype.clear = function() {
3625    if (this.explicitlyCleared) return;
3626    this.explicitlyCleared = true;
3627    for (var i = 0; i < this.markers.length; ++i)
3628      this.markers[i].clear();
3629    signalLater(this, "clear");
3630  };
3631  SharedTextMarker.prototype.find = function() {
3632    return this.primary.find();
3633  };
3634  SharedTextMarker.prototype.getOptions = function(copyWidget) {
3635    var inner = this.primary.getOptions(copyWidget);
3636    inner.shared = true;
3637    return inner;
3638  };
3639
3640  function markTextShared(doc, from, to, options, type) {
3641    options = copyObj(options);
3642    options.shared = false;
3643    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3644    var widget = options.replacedWith;
3645    linkedDocs(doc, function(doc) {
3646      if (widget) options.replacedWith = widget.cloneNode(true);
3647      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3648      for (var i = 0; i < doc.linked.length; ++i)
3649        if (doc.linked[i].isParent) return;
3650      primary = lst(markers);
3651    });
3652    return new SharedTextMarker(markers, primary);
3653  }
3654
3655  // TEXTMARKER SPANS
3656
3657  function getMarkedSpanFor(spans, marker) {
3658    if (spans) for (var i = 0; i < spans.length; ++i) {
3659      var span = spans[i];
3660      if (span.marker == marker) return span;
3661    }
3662  }
3663  function removeMarkedSpan(spans, span) {
3664    for (var r, i = 0; i < spans.length; ++i)
3665      if (spans[i] != span) (r || (r = [])).push(spans[i]);
3666    return r;
3667  }
3668  function addMarkedSpan(line, span) {
3669    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3670    span.marker.attachLine(line);
3671  }
3672
3673  function markedSpansBefore(old, startCh, isInsert) {
3674    if (old) for (var i = 0, nw; i < old.length; ++i) {
3675      var span = old[i], marker = span.marker;
3676      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3677      if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3678        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3679        (nw || (nw = [])).push({from: span.from,
3680                                to: endsAfter ? null : span.to,
3681                                marker: marker});
3682      }
3683    }
3684    return nw;
3685  }
3686
3687  function markedSpansAfter(old, endCh, isInsert) {
3688    if (old) for (var i = 0, nw; i < old.length; ++i) {
3689      var span = old[i], marker = span.marker;
3690      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3691      if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3692        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3693        (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3694                                to: span.to == null ? null : span.to - endCh,
3695                                marker: marker});
3696      }
3697    }
3698    return nw;
3699  }
3700
3701  function stretchSpansOverChange(doc, change) {
3702    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3703    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3704    if (!oldFirst && !oldLast) return null;
3705
3706    var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3707    // Get the spans that 'stick out' on both sides
3708    var first = markedSpansBefore(oldFirst, startCh, isInsert);
3709    var last = markedSpansAfter(oldLast, endCh, isInsert);
3710
3711    // Next, merge those two ends
3712    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3713    if (first) {
3714      // Fix up .to properties of first
3715      for (var i = 0; i < first.length; ++i) {
3716        var span = first[i];
3717        if (span.to == null) {
3718          var found = getMarkedSpanFor(last, span.marker);
3719          if (!found) span.to = startCh;
3720          else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3721        }
3722      }
3723    }
3724    if (last) {
3725      // Fix up .from in last (or move them into first in case of sameLine)
3726      for (var i = 0; i < last.length; ++i) {
3727        var span = last[i];
3728        if (span.to != null) span.to += offset;
3729        if (span.from == null) {
3730          var found = getMarkedSpanFor(first, span.marker);
3731          if (!found) {
3732            span.from = offset;
3733            if (sameLine) (first || (first = [])).push(span);
3734          }
3735        } else {
3736          span.from += offset;
3737          if (sameLine) (first || (first = [])).push(span);
3738        }
3739      }
3740    }
3741
3742    var newMarkers = [first];
3743    if (!sameLine) {
3744      // Fill gap with whole-line-spans
3745      var gap = change.text.length - 2, gapMarkers;
3746      if (gap > 0 && first)
3747        for (var i = 0; i < first.length; ++i)
3748          if (first[i].to == null)
3749            (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3750      for (var i = 0; i < gap; ++i)
3751        newMarkers.push(gapMarkers);
3752      newMarkers.push(last);
3753    }
3754    return newMarkers;
3755  }
3756
3757  function mergeOldSpans(doc, change) {
3758    var old = getOldSpans(doc, change);
3759    var stretched = stretchSpansOverChange(doc, change);
3760    if (!old) return stretched;
3761    if (!stretched) return old;
3762
3763    for (var i = 0; i < old.length; ++i) {
3764      var oldCur = old[i], stretchCur = stretched[i];
3765      if (oldCur && stretchCur) {
3766        spans: for (var j = 0; j < stretchCur.length; ++j) {
3767          var span = stretchCur[j];
3768          for (var k = 0; k < oldCur.length; ++k)
3769            if (oldCur[k].marker == span.marker) continue spans;
3770          oldCur.push(span);
3771        }
3772      } else if (stretchCur) {
3773        old[i] = stretchCur;
3774      }
3775    }
3776    return old;
3777  }
3778
3779  function removeReadOnlyRanges(doc, from, to) {
3780    var markers = null;
3781    doc.iter(from.line, to.line + 1, function(line) {
3782      if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3783        var mark = line.markedSpans[i].marker;
3784        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3785          (markers || (markers = [])).push(mark);
3786      }
3787    });
3788    if (!markers) return null;
3789    var parts = [{from: from, to: to}];
3790    for (var i = 0; i < markers.length; ++i) {
3791      var mk = markers[i], m = mk.find();
3792      for (var j = 0; j < parts.length; ++j) {
3793        var p = parts[j];
3794        if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
3795        var newParts = [j, 1];
3796        if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
3797          newParts.push({from: p.from, to: m.from});
3798        if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
3799          newParts.push({from: m.to, to: p.to});
3800        parts.splice.apply(parts, newParts);
3801        j += newParts.length - 1;
3802      }
3803    }
3804    return parts;
3805  }
3806
3807  function collapsedSpanAt(line, ch) {
3808    var sps = sawCollapsedSpans && line.markedSpans, found;
3809    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3810      sp = sps[i];
3811      if (!sp.marker.collapsed) continue;
3812      if ((sp.from == null || sp.from < ch) &&
3813          (sp.to == null || sp.to > ch) &&
3814          (!found || found.width < sp.marker.width))
3815        found = sp.marker;
3816    }
3817    return found;
3818  }
3819  function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3820  function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3821
3822  function visualLine(doc, line) {
3823    var merged;
3824    while (merged = collapsedSpanAtStart(line))
3825      line = getLine(doc, merged.find().from.line);
3826    return line;
3827  }
3828
3829  function lineIsHidden(doc, line) {
3830    var sps = sawCollapsedSpans && line.markedSpans;
3831    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3832      sp = sps[i];
3833      if (!sp.marker.collapsed) continue;
3834      if (sp.from == null) return true;
3835      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3836        return true;
3837    }
3838  }
3839  function lineIsHiddenInner(doc, line, span) {
3840    if (span.to == null) {
3841      var end = span.marker.find().to, endLine = getLine(doc, end.line);
3842      return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3843    }
3844    if (span.marker.inclusiveRight && span.to == line.text.length)
3845      return true;
3846    for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3847      sp = line.markedSpans[i];
3848      if (sp.marker.collapsed && sp.from == span.to &&
3849          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3850          lineIsHiddenInner(doc, line, sp)) return true;
3851    }
3852  }
3853
3854  function detachMarkedSpans(line) {
3855    var spans = line.markedSpans;
3856    if (!spans) return;
3857    for (var i = 0; i < spans.length; ++i)
3858      spans[i].marker.detachLine(line);
3859    line.markedSpans = null;
3860  }
3861
3862  function attachMarkedSpans(line, spans) {
3863    if (!spans) return;
3864    for (var i = 0; i < spans.length; ++i)
3865      spans[i].marker.attachLine(line);
3866    line.markedSpans = spans;
3867  }
3868
3869  // LINE WIDGETS
3870
3871  var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3872    for (var opt in options) if (options.hasOwnProperty(opt))
3873      this[opt] = options[opt];
3874    this.cm = cm;
3875    this.node = node;
3876  };
3877  function widgetOperation(f) {
3878    return function() {
3879      var withOp = !this.cm.curOp;
3880      if (withOp) startOperation(this.cm);
3881      try {var result = f.apply(this, arguments);}
3882      finally {if (withOp) endOperation(this.cm);}
3883      return result;
3884    };
3885  }
3886  LineWidget.prototype.clear = widgetOperation(function() {
3887    var ws = this.line.widgets, no = lineNo(this.line);
3888    if (no == null || !ws) return;
3889    for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3890    if (!ws.length) this.line.widgets = null;
3891    updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3892    regChange(this.cm, no, no + 1);
3893  });
3894  LineWidget.prototype.changed = widgetOperation(function() {
3895    var oldH = this.height;
3896    this.height = null;
3897    var diff = widgetHeight(this) - oldH;
3898    if (!diff) return;
3899    updateLineHeight(this.line, this.line.height + diff);
3900    var no = lineNo(this.line);
3901    regChange(this.cm, no, no + 1);
3902  });
3903
3904  function widgetHeight(widget) {
3905    if (widget.height != null) return widget.height;
3906    if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3907      removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3908    return widget.height = widget.node.offsetHeight;
3909  }
3910
3911  function addLineWidget(cm, handle, node, options) {
3912    var widget = new LineWidget(cm, node, options);
3913    if (widget.noHScroll) cm.display.alignWidgets = true;
3914    changeLine(cm, handle, function(line) {
3915      (line.widgets || (line.widgets = [])).push(widget);
3916      widget.line = line;
3917      if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
3918        var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3919        updateLineHeight(line, line.height + widgetHeight(widget));
3920        if (aboveVisible) addToScrollPos(cm, 0, widget.height);
3921      }
3922      return true;
3923    });
3924    return widget;
3925  }
3926
3927  // LINE DATA STRUCTURE
3928
3929  // Line objects. These hold state related to a line, including
3930  // highlighting info (the styles array).
3931  function makeLine(text, markedSpans, estimateHeight) {
3932    var line = {text: text};
3933    attachMarkedSpans(line, markedSpans);
3934    line.height = estimateHeight ? estimateHeight(line) : 1;
3935    return line;
3936  }
3937
3938  function updateLine(line, text, markedSpans, estimateHeight) {
3939    line.text = text;
3940    if (line.stateAfter) line.stateAfter = null;
3941    if (line.styles) line.styles = null;
3942    if (line.order != null) line.order = null;
3943    detachMarkedSpans(line);
3944    attachMarkedSpans(line, markedSpans);
3945    var estHeight = estimateHeight ? estimateHeight(line) : 1;
3946    if (estHeight != line.height) updateLineHeight(line, estHeight);
3947  }
3948
3949  function cleanUpLine(line) {
3950    line.parent = null;
3951    detachMarkedSpans(line);
3952  }
3953
3954  // Run the given mode's parser over a line, update the styles
3955  // array, which contains alternating fragments of text and CSS
3956  // classes.
3957  function runMode(cm, text, mode, state, f) {
3958    var flattenSpans = mode.flattenSpans;
3959    if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
3960    var curText = "", curStyle = null;
3961    var stream = new StringStream(text, cm.options.tabSize);
3962    if (text == "" && mode.blankLine) mode.blankLine(state);
3963    while (!stream.eol()) {
3964      var style = mode.token(stream, state);
3965      if (stream.pos > 5000) {
3966        flattenSpans = false;
3967        // Webkit seems to refuse to render text nodes longer than 57444 characters
3968        stream.pos = Math.min(text.length, stream.start + 50000);
3969        style = null;
3970      }
3971      var substr = stream.current();
3972      stream.start = stream.pos;
3973      if (!flattenSpans || curStyle != style) {
3974        if (curText) f(curText, curStyle);
3975        curText = substr; curStyle = style;
3976      } else curText = curText + substr;
3977    }
3978    if (curText) f(curText, curStyle);
3979  }
3980
3981  function highlightLine(cm, line, state) {
3982    // A styles array always starts with a number identifying the
3983    // mode/overlays that it is based on (for easy invalidation).
3984    var st = [cm.state.modeGen];
3985    // Compute the base array of styles
3986    runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
3987
3988    // Run overlays, adjust style array.
3989    for (var o = 0; o < cm.state.overlays.length; ++o) {
3990      var overlay = cm.state.overlays[o], i = 1;
3991      runMode(cm, line.text, overlay.mode, true, function(txt, style) {
3992        var start = i, len = txt.length;
3993        // Ensure there's a token end at the current position, and that i points at it
3994        while (len) {
3995          var cur = st[i], len_ = cur.length;
3996          if (len_ <= len) {
3997            len -= len_;
3998          } else {
3999            st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
4000            len = 0;
4001          }
4002          i += 2;
4003        }
4004        if (!style) return;
4005        if (overlay.opaque) {
4006          st.splice(start, i - start, txt, style);
4007          i = start + 2;
4008        } else {
4009          for (; start < i; start += 2) {
4010            var cur = st[start+1];
4011            st[start+1] = cur ? cur + " " + style : style;
4012          }
4013        }
4014      });
4015    }
4016
4017    return st;
4018  }
4019
4020  function getLineStyles(cm, line) {
4021    if (!line.styles || line.styles[0] != cm.state.modeGen)
4022      line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4023    return line.styles;
4024  }
4025
4026  // Lightweight form of highlight -- proceed over this line and
4027  // update state, but don't save a style array.
4028  function processLine(cm, line, state) {
4029    var mode = cm.doc.mode;
4030    var stream = new StringStream(line.text, cm.options.tabSize);
4031    if (line.text == "" && mode.blankLine) mode.blankLine(state);
4032    while (!stream.eol() && stream.pos <= 5000) {
4033      mode.token(stream, state);
4034      stream.start = stream.pos;
4035    }
4036  }
4037
4038  var styleToClassCache = {};
4039  function styleToClass(style) {
4040    if (!style) return null;
4041    return styleToClassCache[style] ||
4042      (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
4043  }
4044
4045  function lineContent(cm, realLine, measure) {
4046    var merged, line = realLine, lineBefore, sawBefore, simple = true;
4047    while (merged = collapsedSpanAtStart(line)) {
4048      simple = false;
4049      line = getLine(cm.doc, merged.find().from.line);
4050      if (!lineBefore) lineBefore = line;
4051    }
4052
4053    var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
4054                   measure: null, addedOne: false, cm: cm};
4055    if (line.textClass) builder.pre.className = line.textClass;
4056
4057    do {
4058      builder.measure = line == realLine && measure;
4059      builder.pos = 0;
4060      builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4061      if ((ie || webkit) && cm.getOption("lineWrapping"))
4062        builder.addToken = buildTokenSplitSpaces(builder.addToken);
4063      if (measure && sawBefore && line != realLine && !builder.addedOne) {
4064        measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4065        builder.addedOne = true;
4066      }
4067      var next = insertLineContent(line, builder, getLineStyles(cm, line));
4068      sawBefore = line == lineBefore;
4069      if (next) {
4070        line = getLine(cm.doc, next.to.line);
4071        simple = false;
4072      }
4073    } while (next);
4074
4075    if (measure && !builder.addedOne)
4076      measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4077    if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4078      builder.pre.appendChild(document.createTextNode("\u00a0"));
4079
4080    var order;
4081    // Work around problem with the reported dimensions of single-char
4082    // direction spans on IE (issue #1129). See also the comment in
4083    // cursorCoords.
4084    if (measure && ie && (order = getOrder(line))) {
4085      var l = order.length - 1;
4086      if (order[l].from == order[l].to) --l;
4087      var last = order[l], prev = order[l - 1];
4088      if (last.from + 1 == last.to && prev && last.level < prev.level) {
4089        var span = measure[builder.pos - 1];
4090        if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4091                                               span.nextSibling);
4092      }
4093    }
4094
4095    signal(cm, "renderLine", cm, realLine, builder.pre);
4096    return builder.pre;
4097  }
4098
4099  var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4100  function buildToken(builder, text, style, startStyle, endStyle) {
4101    if (!text) return;
4102    if (!tokenSpecialChars.test(text)) {
4103      builder.col += text.length;
4104      var content = document.createTextNode(text);
4105    } else {
4106      var content = document.createDocumentFragment(), pos = 0;
4107      while (true) {
4108        tokenSpecialChars.lastIndex = pos;
4109        var m = tokenSpecialChars.exec(text);
4110        var skipped = m ? m.index - pos : text.length - pos;
4111        if (skipped) {
4112          content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
4113          builder.col += skipped;
4114        }
4115        if (!m) break;
4116        pos += skipped + 1;
4117        if (m[0] == "\t") {
4118          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4119          content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4120          builder.col += tabWidth;
4121        } else {
4122          var token = elt("span", "\u2022", "cm-invalidchar");
4123          token.title = "\\u" + m[0].charCodeAt(0).toString(16);
4124          content.appendChild(token);
4125          builder.col += 1;
4126        }
4127      }
4128    }
4129    if (style || startStyle || endStyle || builder.measure) {
4130      var fullStyle = style || "";
4131      if (startStyle) fullStyle += startStyle;
4132      if (endStyle) fullStyle += endStyle;
4133      return builder.pre.appendChild(elt("span", [content], fullStyle));
4134    }
4135    builder.pre.appendChild(content);
4136  }
4137
4138  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4139    var wrapping = builder.cm.options.lineWrapping;
4140    for (var i = 0; i < text.length; ++i) {
4141      var ch = text.charAt(i), start = i == 0;
4142      if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4143        ch = text.slice(i, i + 2);
4144        ++i;
4145      } else if (i && wrapping &&
4146                 spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
4147        builder.pre.appendChild(elt("wbr"));
4148      }
4149      var span = builder.measure[builder.pos] =
4150        buildToken(builder, ch, style,
4151                   start && startStyle, i == text.length - 1 && endStyle);
4152      // In IE single-space nodes wrap differently than spaces
4153      // embedded in larger text nodes, except when set to
4154      // white-space: normal (issue #1268).
4155      if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4156          i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4157        span.style.whiteSpace = "normal";
4158      builder.pos += ch.length;
4159    }
4160    if (text.length) builder.addedOne = true;
4161  }
4162
4163  function buildTokenSplitSpaces(inner) {
4164    function split(old) {
4165      var out = " ";
4166      for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4167      out += " ";
4168      return out;
4169    }
4170    return function(builder, text, style, startStyle, endStyle) {
4171      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
4172    };
4173  }
4174
4175  function buildCollapsedSpan(builder, size, widget) {
4176    if (widget) {
4177      if (!builder.display) widget = widget.cloneNode(true);
4178      builder.pre.appendChild(widget);
4179      if (builder.measure && size) {
4180        builder.measure[builder.pos] = widget;
4181        builder.addedOne = true;
4182      }
4183    }
4184    builder.pos += size;
4185  }
4186
4187  // Outputs a number of spans to make up a line, taking highlighting
4188  // and marked text into account.
4189  function insertLineContent(line, builder, styles) {
4190    var spans = line.markedSpans;
4191    if (!spans) {
4192      for (var i = 1; i < styles.length; i+=2)
4193        builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
4194      return;
4195    }
4196
4197    var allText = line.text, len = allText.length;
4198    var pos = 0, i = 1, text = "", style;
4199    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4200    for (;;) {
4201      if (nextChange == pos) { // Update current marker set
4202        spanStyle = spanEndStyle = spanStartStyle = "";
4203        collapsed = null; nextChange = Infinity;
4204        var foundBookmark = null;
4205        for (var j = 0; j < spans.length; ++j) {
4206          var sp = spans[j], m = sp.marker;
4207          if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
4208            if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
4209            if (m.className) spanStyle += " " + m.className;
4210            if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4211            if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4212            if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
4213              collapsed = sp;
4214          } else if (sp.from > pos && nextChange > sp.from) {
4215            nextChange = sp.from;
4216          }
4217          if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
4218            foundBookmark = m.replacedWith;
4219        }
4220        if (collapsed && (collapsed.from || 0) == pos) {
4221          buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
4222                             collapsed.from != null && collapsed.marker.replacedWith);
4223          if (collapsed.to == null) return collapsed.marker.find();
4224        }
4225        if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
4226      }
4227      if (pos >= len) break;
4228
4229      var upto = Math.min(len, nextChange);
4230      while (true) {
4231        if (text) {
4232          var end = pos + text.length;
4233          if (!collapsed) {
4234            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4235            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4236                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
4237          }
4238          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4239          pos = end;
4240          spanStartStyle = "";
4241        }
4242        text = styles[i++]; style = styleToClass(styles[i++]);
4243      }
4244    }
4245  }
4246
4247  // DOCUMENT DATA STRUCTURE
4248
4249  function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4250    function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4251    function update(line, text, spans) {
4252      updateLine(line, text, spans, estimateHeight);
4253      signalLater(line, "change", line, change);
4254    }
4255
4256    var from = change.from, to = change.to, text = change.text;
4257    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4258    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4259
4260    // First adjust the line structure
4261    if (from.ch == 0 && to.ch == 0 && lastText == "") {
4262      // This is a whole-line replace. Treated specially to make
4263      // sure line objects move the way they are supposed to.
4264      for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4265        added.push(makeLine(text[i], spansFor(i), estimateHeight));
4266      update(lastLine, lastLine.text, lastSpans);
4267      if (nlines) doc.remove(from.line, nlines);
4268      if (added.length) doc.insert(from.line, added);
4269    } else if (firstLine == lastLine) {
4270      if (text.length == 1) {
4271        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4272      } else {
4273        for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4274          added.push(makeLine(text[i], spansFor(i), estimateHeight));
4275        added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4276        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4277        doc.insert(from.line + 1, added);
4278      }
4279    } else if (text.length == 1) {
4280      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4281      doc.remove(from.line + 1, nlines);
4282    } else {
4283      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4284      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4285      for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4286        added.push(makeLine(text[i], spansFor(i), estimateHeight));
4287      if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4288      doc.insert(from.line + 1, added);
4289    }
4290
4291    signalLater(doc, "change", doc, change);
4292    setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4293  }
4294
4295  function LeafChunk(lines) {
4296    this.lines = lines;
4297    this.parent = null;
4298    for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
4299      lines[i].parent = this;
4300      height += lines[i].height;
4301    }
4302    this.height = height;
4303  }
4304
4305  LeafChunk.prototype = {
4306    chunkSize: function() { return this.lines.length; },
4307    removeInner: function(at, n) {
4308      for (var i = at, e = at + n; i < e; ++i) {
4309        var line = this.lines[i];
4310        this.height -= line.height;
4311        cleanUpLine(line);
4312        signalLater(line, "delete");
4313      }
4314      this.lines.splice(at, n);
4315    },
4316    collapse: function(lines) {
4317      lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4318    },
4319    insertInner: function(at, lines, height) {
4320      this.height += height;
4321      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4322      for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4323    },
4324    iterN: function(at, n, op) {
4325      for (var e = at + n; at < e; ++at)
4326        if (op(this.lines[at])) return true;
4327    }
4328  };
4329
4330  function BranchChunk(children) {
4331    this.children = children;
4332    var size = 0, height = 0;
4333    for (var i = 0, e = children.length; i < e; ++i) {
4334      var ch = children[i];
4335      size += ch.chunkSize(); height += ch.height;
4336      ch.parent = this;
4337    }
4338    this.size = size;
4339    this.height = height;
4340    this.parent = null;
4341  }
4342
4343  BranchChunk.prototype = {
4344    chunkSize: function() { return this.size; },
4345    removeInner: function(at, n) {
4346      this.size -= n;
4347      for (var i = 0; i < this.children.length; ++i) {
4348        var child = this.children[i], sz = child.chunkSize();
4349        if (at < sz) {
4350          var rm = Math.min(n, sz - at), oldHeight = child.height;
4351          child.removeInner(at, rm);
4352          this.height -= oldHeight - child.height;
4353          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4354          if ((n -= rm) == 0) break;
4355          at = 0;
4356        } else at -= sz;
4357      }
4358      if (this.size - n < 25) {
4359        var lines = [];
4360        this.collapse(lines);
4361        this.children = [new LeafChunk(lines)];
4362        this.children[0].parent = this;
4363      }
4364    },
4365    collapse: function(lines) {
4366      for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4367    },
4368    insertInner: function(at, lines, height) {
4369      this.size += lines.length;
4370      this.height += height;
4371      for (var i = 0, e = this.children.length; i < e; ++i) {
4372        var child = this.children[i], sz = child.chunkSize();
4373        if (at <= sz) {
4374          child.insertInner(at, lines, height);
4375          if (child.lines && child.lines.length > 50) {
4376            while (child.lines.length > 50) {
4377              var spilled = child.lines.splice(child.lines.length - 25, 25);
4378              var newleaf = new LeafChunk(spilled);
4379              child.height -= newleaf.height;
4380              this.children.splice(i + 1, 0, newleaf);
4381              newleaf.parent = this;
4382            }
4383            this.maybeSpill();
4384          }
4385          break;
4386        }
4387        at -= sz;
4388      }
4389    },
4390    maybeSpill: function() {
4391      if (this.children.length <= 10) return;
4392      var me = this;
4393      do {
4394        var spilled = me.children.splice(me.children.length - 5, 5);
4395        var sibling = new BranchChunk(spilled);
4396        if (!me.parent) { // Become the parent node
4397          var copy = new BranchChunk(me.children);
4398          copy.parent = me;
4399          me.children = [copy, sibling];
4400          me = copy;
4401        } else {
4402          me.size -= sibling.size;
4403          me.height -= sibling.height;
4404          var myIndex = indexOf(me.parent.children, me);
4405          me.parent.children.splice(myIndex + 1, 0, sibling);
4406        }
4407        sibling.parent = me.parent;
4408      } while (me.children.length > 10);
4409      me.parent.maybeSpill();
4410    },
4411    iterN: function(at, n, op) {
4412      for (var i = 0, e = this.children.length; i < e; ++i) {
4413        var child = this.children[i], sz = child.chunkSize();
4414        if (at < sz) {
4415          var used = Math.min(n, sz - at);
4416          if (child.iterN(at, used, op)) return true;
4417          if ((n -= used) == 0) break;
4418          at = 0;
4419        } else at -= sz;
4420      }
4421    }
4422  };
4423
4424  var nextDocId = 0;
4425  var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4426    if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4427    if (firstLine == null) firstLine = 0;
4428
4429    BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
4430    this.first = firstLine;
4431    this.scrollTop = this.scrollLeft = 0;
4432    this.cantEdit = false;
4433    this.history = makeHistory();
4434    this.frontier = firstLine;
4435    var start = Pos(firstLine, 0);
4436    this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4437    this.id = ++nextDocId;
4438    this.modeOption = mode;
4439
4440    if (typeof text == "string") text = splitLines(text);
4441    updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4442  };
4443
4444  Doc.prototype = createObj(BranchChunk.prototype, {
4445    iter: function(from, to, op) {
4446      if (op) this.iterN(from - this.first, to - from, op);
4447      else this.iterN(this.first, this.first + this.size, from);
4448    },
4449
4450    insert: function(at, lines) {
4451      var height = 0;
4452      for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4453      this.insertInner(at - this.first, lines, height);
4454    },
4455    remove: function(at, n) { this.removeInner(at - this.first, n); },
4456
4457    getValue: function(lineSep) {
4458      var lines = getLines(this, this.first, this.first + this.size);
4459      if (lineSep === false) return lines;
4460      return lines.join(lineSep || "\n");
4461    },
4462    setValue: function(code) {
4463      var top = Pos(this.first, 0), last = this.first + this.size - 1;
4464      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4465                        text: splitLines(code), origin: "setValue"},
4466                 {head: top, anchor: top}, true);
4467    },
4468    replaceRange: function(code, from, to, origin) {
4469      from = clipPos(this, from);
4470      to = to ? clipPos(this, to) : from;
4471      replaceRange(this, code, from, to, origin);
4472    },
4473    getRange: function(from, to, lineSep) {
4474      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4475      if (lineSep === false) return lines;
4476      return lines.join(lineSep || "\n");
4477    },
4478
4479    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4480    setLine: function(line, text) {
4481      if (isLine(this, line))
4482        replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4483    },
4484    removeLine: function(line) {
4485      if (isLine(this, line))
4486        replaceRange(this, "", Pos(line, 0), clipPos(this, Pos(line + 1, 0)));
4487    },
4488
4489    getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4490    getLineNumber: function(line) {return lineNo(line);},
4491
4492    lineCount: function() {return this.size;},
4493    firstLine: function() {return this.first;},
4494    lastLine: function() {return this.first + this.size - 1;},
4495
4496    clipPos: function(pos) {return clipPos(this, pos);},
4497
4498    getCursor: function(start) {
4499      var sel = this.sel, pos;
4500      if (start == null || start == "head") pos = sel.head;
4501      else if (start == "anchor") pos = sel.anchor;
4502      else if (start == "end" || start === false) pos = sel.to;
4503      else pos = sel.from;
4504      return copyPos(pos);
4505    },
4506    somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4507
4508    setCursor: docOperation(function(line, ch, extend) {
4509      var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4510      if (extend) extendSelection(this, pos);
4511      else setSelection(this, pos, pos);
4512    }),
4513    setSelection: docOperation(function(anchor, head) {
4514      setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
4515    }),
4516    extendSelection: docOperation(function(from, to) {
4517      extendSelection(this, clipPos(this, from), to && clipPos(this, to));
4518    }),
4519
4520    getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4521    replaceSelection: function(code, collapse, origin) {
4522      makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4523    },
4524    undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4525    redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4526
4527    setExtending: function(val) {this.sel.extend = val;},
4528
4529    historySize: function() {
4530      var hist = this.history;
4531      return {undo: hist.done.length, redo: hist.undone.length};
4532    },
4533    clearHistory: function() {this.history = makeHistory();},
4534
4535    markClean: function() {
4536      this.history.dirtyCounter = 0;
4537      this.history.lastOp = this.history.lastOrigin = null;
4538    },
4539    isClean: function () {return this.history.dirtyCounter == 0;},
4540
4541    getHistory: function() {
4542      return {done: copyHistoryArray(this.history.done),
4543              undone: copyHistoryArray(this.history.undone)};
4544    },
4545    setHistory: function(histData) {
4546      var hist = this.history = makeHistory();
4547      hist.done = histData.done.slice(0);
4548      hist.undone = histData.undone.slice(0);
4549    },
4550
4551    markText: function(from, to, options) {
4552      return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4553    },
4554    setBookmark: function(pos, options) {
4555      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4556                      insertLeft: options && options.insertLeft};
4557      pos = clipPos(this, pos);
4558      return markText(this, pos, pos, realOpts, "bookmark");
4559    },
4560    findMarksAt: function(pos) {
4561      pos = clipPos(this, pos);
4562      var markers = [], spans = getLine(this, pos.line).markedSpans;
4563      if (spans) for (var i = 0; i < spans.length; ++i) {
4564        var span = spans[i];
4565        if ((span.from == null || span.from <= pos.ch) &&
4566            (span.to == null || span.to >= pos.ch))
4567          markers.push(span.marker.parent || span.marker);
4568      }
4569      return markers;
4570    },
4571    getAllMarks: function() {
4572      var markers = [];
4573      this.iter(function(line) {
4574        var sps = line.markedSpans;
4575        if (sps) for (var i = 0; i < sps.length; ++i)
4576          if (sps[i].from != null) markers.push(sps[i].marker);
4577      });
4578      return markers;
4579    },
4580
4581    posFromIndex: function(off) {
4582      var ch, lineNo = this.first;
4583      this.iter(function(line) {
4584        var sz = line.text.length + 1;
4585        if (sz > off) { ch = off; return true; }
4586        off -= sz;
4587        ++lineNo;
4588      });
4589      return clipPos(this, Pos(lineNo, ch));
4590    },
4591    indexFromPos: function (coords) {
4592      coords = clipPos(this, coords);
4593      var index = coords.ch;
4594      if (coords.line < this.first || coords.ch < 0) return 0;
4595      this.iter(this.first, coords.line, function (line) {
4596        index += line.text.length + 1;
4597      });
4598      return index;
4599    },
4600
4601    copy: function(copyHistory) {
4602      var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4603      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4604      doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4605                 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4606      if (copyHistory) {
4607        doc.history.undoDepth = this.history.undoDepth;
4608        doc.setHistory(this.getHistory());
4609      }
4610      return doc;
4611    },
4612
4613    linkedDoc: function(options) {
4614      if (!options) options = {};
4615      var from = this.first, to = this.first + this.size;
4616      if (options.from != null && options.from > from) from = options.from;
4617      if (options.to != null && options.to < to) to = options.to;
4618      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4619      if (options.sharedHist) copy.history = this.history;
4620      (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4621      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4622      return copy;
4623    },
4624    unlinkDoc: function(other) {
4625      if (other instanceof CodeMirror) other = other.doc;
4626      if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4627        var link = this.linked[i];
4628        if (link.doc != other) continue;
4629        this.linked.splice(i, 1);
4630        other.unlinkDoc(this);
4631        break;
4632      }
4633      // If the histories were shared, split them again
4634      if (other.history == this.history) {
4635        var splitIds = [other.id];
4636        linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4637        other.history = makeHistory();
4638        other.history.done = copyHistoryArray(this.history.done, splitIds);
4639        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4640      }
4641    },
4642    iterLinkedDocs: function(f) {linkedDocs(this, f);},
4643
4644    getMode: function() {return this.mode;},
4645    getEditor: function() {return this.cm;}
4646  });
4647
4648  Doc.prototype.eachLine = Doc.prototype.iter;
4649
4650  // The Doc methods that should be available on CodeMirror instances
4651  var dontDelegate = "iter insert remove copy getEditor".split(" ");
4652  for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4653    CodeMirror.prototype[prop] = (function(method) {
4654      return function() {return method.apply(this.doc, arguments);};
4655    })(Doc.prototype[prop]);
4656
4657  function linkedDocs(doc, f, sharedHistOnly) {
4658    function propagate(doc, skip, sharedHist) {
4659      if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4660        var rel = doc.linked[i];
4661        if (rel.doc == skip) continue;
4662        var shared = sharedHist && rel.sharedHist;
4663        if (sharedHistOnly && !shared) continue;
4664        f(rel.doc, shared);
4665        propagate(rel.doc, doc, shared);
4666      }
4667    }
4668    propagate(doc, null, true);
4669  }
4670
4671  function attachDoc(cm, doc) {
4672    if (doc.cm) throw new Error("This document is already in use.");
4673    cm.doc = doc;
4674    doc.cm = cm;
4675    estimateLineHeights(cm);
4676    loadMode(cm);
4677    if (!cm.options.lineWrapping) computeMaxLength(cm);
4678    cm.options.mode = doc.modeOption;
4679    regChange(cm);
4680  }
4681
4682  // LINE UTILITIES
4683
4684  function getLine(chunk, n) {
4685    n -= chunk.first;
4686    while (!chunk.lines) {
4687      for (var i = 0;; ++i) {
4688        var child = chunk.children[i], sz = child.chunkSize();
4689        if (n < sz) { chunk = child; break; }
4690        n -= sz;
4691      }
4692    }
4693    return chunk.lines[n];
4694  }
4695
4696  function getBetween(doc, start, end) {
4697    var out = [], n = start.line;
4698    doc.iter(start.line, end.line + 1, function(line) {
4699      var text = line.text;
4700      if (n == end.line) text = text.slice(0, end.ch);
4701      if (n == start.line) text = text.slice(start.ch);
4702      out.push(text);
4703      ++n;
4704    });
4705    return out;
4706  }
4707  function getLines(doc, from, to) {
4708    var out = [];
4709    doc.iter(from, to, function(line) { out.push(line.text); });
4710    return out;
4711  }
4712
4713  function updateLineHeight(line, height) {
4714    var diff = height - line.height;
4715    for (var n = line; n; n = n.parent) n.height += diff;
4716  }
4717
4718  function lineNo(line) {
4719    if (line.parent == null) return null;
4720    var cur = line.parent, no = indexOf(cur.lines, line);
4721    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4722      for (var i = 0;; ++i) {
4723        if (chunk.children[i] == cur) break;
4724        no += chunk.children[i].chunkSize();
4725      }
4726    }
4727    return no + cur.first;
4728  }
4729
4730  function lineAtHeight(chunk, h) {
4731    var n = chunk.first;
4732    outer: do {
4733      for (var i = 0, e = chunk.children.length; i < e; ++i) {
4734        var child = chunk.children[i], ch = child.height;
4735        if (h < ch) { chunk = child; continue outer; }
4736        h -= ch;
4737        n += child.chunkSize();
4738      }
4739      return n;
4740    } while (!chunk.lines);
4741    for (var i = 0, e = chunk.lines.length; i < e; ++i) {
4742      var line = chunk.lines[i], lh = line.height;
4743      if (h < lh) break;
4744      h -= lh;
4745    }
4746    return n + i;
4747  }
4748
4749  function heightAtLine(cm, lineObj) {
4750    lineObj = visualLine(cm.doc, lineObj);
4751
4752    var h = 0, chunk = lineObj.parent;
4753    for (var i = 0; i < chunk.lines.length; ++i) {
4754      var line = chunk.lines[i];
4755      if (line == lineObj) break;
4756      else h += line.height;
4757    }
4758    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4759      for (var i = 0; i < p.children.length; ++i) {
4760        var cur = p.children[i];
4761        if (cur == chunk) break;
4762        else h += cur.height;
4763      }
4764    }
4765    return h;
4766  }
4767
4768  function getOrder(line) {
4769    var order = line.order;
4770    if (order == null) order = line.order = bidiOrdering(line.text);
4771    return order;
4772  }
4773
4774  // HISTORY
4775
4776  function makeHistory() {
4777    return {
4778      // Arrays of history events. Doing something adds an event to
4779      // done and clears undo. Undoing moves events from done to
4780      // undone, redoing moves them in the other direction.
4781      done: [], undone: [], undoDepth: Infinity,
4782      // Used to track when changes can be merged into a single undo
4783      // event
4784      lastTime: 0, lastOp: null, lastOrigin: null,
4785      // Used by the isClean() method
4786      dirtyCounter: 0
4787    };
4788  }
4789
4790  function attachLocalSpans(doc, change, from, to) {
4791    var existing = change["spans_" + doc.id], n = 0;
4792    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4793      if (line.markedSpans)
4794        (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4795      ++n;
4796    });
4797  }
4798
4799  function historyChangeFromChange(doc, change) {
4800    var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4801    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4802    linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
4803    return histChange;
4804  }
4805
4806  function addToHistory(doc, change, selAfter, opId) {
4807    var hist = doc.history;
4808    hist.undone.length = 0;
4809    var time = +new Date, cur = lst(hist.done);
4810
4811    if (cur &&
4812        (hist.lastOp == opId ||
4813         hist.lastOrigin == change.origin && change.origin &&
4814         ((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) {
4815      // Merge this change into the last event
4816      var last = lst(cur.changes);
4817      if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
4818        // Optimized case for simple insertion -- don't want to add
4819        // new changesets for every character typed
4820        last.to = changeEnd(change);
4821      } else {
4822        // Add new sub-event
4823        cur.changes.push(historyChangeFromChange(doc, change));
4824      }
4825      cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
4826    } else {
4827      // Can not be merged, start a new event.
4828      cur = {changes: [historyChangeFromChange(doc, change)],
4829             anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4830             anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4831      hist.done.push(cur);
4832      while (hist.done.length > hist.undoDepth)
4833        hist.done.shift();
4834      if (hist.dirtyCounter < 0)
4835        // The user has made a change after undoing past the last clean state.
4836        // We can never get back to a clean state now until markClean() is called.
4837        hist.dirtyCounter = NaN;
4838      else
4839        hist.dirtyCounter++;
4840    }
4841    hist.lastTime = time;
4842    hist.lastOp = opId;
4843    hist.lastOrigin = change.origin;
4844  }
4845
4846  function removeClearedSpans(spans) {
4847    if (!spans) return null;
4848    for (var i = 0, out; i < spans.length; ++i) {
4849      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
4850      else if (out) out.push(spans[i]);
4851    }
4852    return !out ? spans : out.length ? out : null;
4853  }
4854
4855  function getOldSpans(doc, change) {
4856    var found = change["spans_" + doc.id];
4857    if (!found) return null;
4858    for (var i = 0, nw = []; i < change.text.length; ++i)
4859      nw.push(removeClearedSpans(found[i]));
4860    return nw;
4861  }
4862
4863  // Used both to provide a JSON-safe object in .getHistory, and, when
4864  // detaching a document, to split the history in two
4865  function copyHistoryArray(events, newGroup) {
4866    for (var i = 0, copy = []; i < events.length; ++i) {
4867      var event = events[i], changes = event.changes, newChanges = [];
4868      copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
4869                 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
4870      for (var j = 0; j < changes.length; ++j) {
4871        var change = changes[j], m;
4872        newChanges.push({from: change.from, to: change.to, text: change.text});
4873        if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
4874          if (indexOf(newGroup, Number(m[1])) > -1) {
4875            lst(newChanges)[prop] = change[prop];
4876            delete change[prop];
4877          }
4878        }
4879      }
4880    }
4881    return copy;
4882  }
4883
4884  // Rebasing/resetting history to deal with externally-sourced changes
4885
4886  function rebaseHistSel(pos, from, to, diff) {
4887    if (to < pos.line) {
4888      pos.line += diff;
4889    } else if (from < pos.line) {
4890      pos.line = from;
4891      pos.ch = 0;
4892    }
4893  }
4894
4895  // Tries to rebase an array of history events given a change in the
4896  // document. If the change touches the same lines as the event, the
4897  // event, and everything 'behind' it, is discarded. If the change is
4898  // before the event, the event's positions are updated. Uses a
4899  // copy-on-write scheme for the positions, to avoid having to
4900  // reallocate them all on every rebase, but also avoid problems with
4901  // shared position objects being unsafely updated.
4902  function rebaseHistArray(array, from, to, diff) {
4903    for (var i = 0; i < array.length; ++i) {
4904      var sub = array[i], ok = true;
4905      for (var j = 0; j < sub.changes.length; ++j) {
4906        var cur = sub.changes[j];
4907        if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
4908        if (to < cur.from.line) {
4909          cur.from.line += diff;
4910          cur.to.line += diff;
4911        } else if (from <= cur.to.line) {
4912          ok = false;
4913          break;
4914        }
4915      }
4916      if (!sub.copied) {
4917        sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
4918        sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
4919        sub.copied = true;
4920      }
4921      if (!ok) {
4922        array.splice(0, i + 1);
4923        i = 0;
4924      } else {
4925        rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
4926        rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
4927      }
4928    }
4929  }
4930
4931  function rebaseHist(hist, change) {
4932    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
4933    rebaseHistArray(hist.done, from, to, diff);
4934    rebaseHistArray(hist.undone, from, to, diff);
4935  }
4936
4937  // EVENT OPERATORS
4938
4939  function stopMethod() {e_stop(this);}
4940  // Ensure an event has a stop method.
4941  function addStop(event) {
4942    if (!event.stop) event.stop = stopMethod;
4943    return event;
4944  }
4945
4946  function e_preventDefault(e) {
4947    if (e.preventDefault) e.preventDefault();
4948    else e.returnValue = false;
4949  }
4950  function e_stopPropagation(e) {
4951    if (e.stopPropagation) e.stopPropagation();
4952    else e.cancelBubble = true;
4953  }
4954  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
4955  CodeMirror.e_stop = e_stop;
4956  CodeMirror.e_preventDefault = e_preventDefault;
4957  CodeMirror.e_stopPropagation = e_stopPropagation;
4958
4959  function e_target(e) {return e.target || e.srcElement;}
4960  function e_button(e) {
4961    var b = e.which;
4962    if (b == null) {
4963      if (e.button & 1) b = 1;
4964      else if (e.button & 2) b = 3;
4965      else if (e.button & 4) b = 2;
4966    }
4967    if (mac && e.ctrlKey && b == 1) b = 3;
4968    return b;
4969  }
4970
4971  // EVENT HANDLING
4972
4973  function on(emitter, type, f) {
4974    if (emitter.addEventListener)
4975      emitter.addEventListener(type, f, false);
4976    else if (emitter.attachEvent)
4977      emitter.attachEvent("on" + type, f);
4978    else {
4979      var map = emitter._handlers || (emitter._handlers = {});
4980      var arr = map[type] || (map[type] = []);
4981      arr.push(f);
4982    }
4983  }
4984
4985  function off(emitter, type, f) {
4986    if (emitter.removeEventListener)
4987      emitter.removeEventListener(type, f, false);
4988    else if (emitter.detachEvent)
4989      emitter.detachEvent("on" + type, f);
4990    else {
4991      var arr = emitter._handlers && emitter._handlers[type];
4992      if (!arr) return;
4993      for (var i = 0; i < arr.length; ++i)
4994        if (arr[i] == f) { arr.splice(i, 1); break; }
4995    }
4996  }
4997
4998  function signal(emitter, type /*, values...*/) {
4999    var arr = emitter._handlers && emitter._handlers[type];
5000    if (!arr) return;
5001    var args = Array.prototype.slice.call(arguments, 2);
5002    for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5003  }
5004
5005  var delayedCallbacks, delayedCallbackDepth = 0;
5006  function signalLater(emitter, type /*, values...*/) {
5007    var arr = emitter._handlers && emitter._handlers[type];
5008    if (!arr) return;
5009    var args = Array.prototype.slice.call(arguments, 2);
5010    if (!delayedCallbacks) {
5011      ++delayedCallbackDepth;
5012      delayedCallbacks = [];
5013      setTimeout(fireDelayed, 0);
5014    }
5015    function bnd(f) {return function(){f.apply(null, args);};};
5016    for (var i = 0; i < arr.length; ++i)
5017      delayedCallbacks.push(bnd(arr[i]));
5018  }
5019
5020  function fireDelayed() {
5021    --delayedCallbackDepth;
5022    var delayed = delayedCallbacks;
5023    delayedCallbacks = null;
5024    for (var i = 0; i < delayed.length; ++i) delayed[i]();
5025  }
5026
5027  function hasHandler(emitter, type) {
5028    var arr = emitter._handlers && emitter._handlers[type];
5029    return arr && arr.length > 0;
5030  }
5031
5032  CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
5033
5034  // MISC UTILITIES
5035
5036  // Number of pixels added to scroller and sizer to hide scrollbar
5037  var scrollerCutOff = 30;
5038
5039  // Returned or thrown by various protocols to signal 'I'm not
5040  // handling this'.
5041  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5042
5043  function Delayed() {this.id = null;}
5044  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
5045
5046  // Counts the column offset in a string, taking tabs into account.
5047  // Used mostly to find indentation.
5048  function countColumn(string, end, tabSize, startIndex, startValue) {
5049    if (end == null) {
5050      end = string.search(/[^\s\u00a0]/);
5051      if (end == -1) end = string.length;
5052    }
5053    for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5054      if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5055      else ++n;
5056    }
5057    return n;
5058  }
5059  CodeMirror.countColumn = countColumn;
5060
5061  var spaceStrs = [""];
5062  function spaceStr(n) {
5063    while (spaceStrs.length <= n)
5064      spaceStrs.push(lst(spaceStrs) + " ");
5065    return spaceStrs[n];
5066  }
5067
5068  function lst(arr) { return arr[arr.length-1]; }
5069
5070  function selectInput(node) {
5071    if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5072      node.selectionStart = 0;
5073      node.selectionEnd = node.value.length;
5074    } else node.select();
5075  }
5076
5077  function indexOf(collection, elt) {
5078    if (collection.indexOf) return collection.indexOf(elt);
5079    for (var i = 0, e = collection.length; i < e; ++i)
5080      if (collection[i] == elt) return i;
5081    return -1;
5082  }
5083
5084  function createObj(base, props) {
5085    function Obj() {}
5086    Obj.prototype = base;
5087    var inst = new Obj();
5088    if (props) copyObj(props, inst);
5089    return inst;
5090  }
5091
5092  function copyObj(obj, target) {
5093    if (!target) target = {};
5094    for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5095    return target;
5096  }
5097
5098  function emptyArray(size) {
5099    for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5100    return a;
5101  }
5102
5103  function bind(f) {
5104    var args = Array.prototype.slice.call(arguments, 1);
5105    return function(){return f.apply(null, args);};
5106  }
5107
5108  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
5109  function isWordChar(ch) {
5110    return /\w/.test(ch) || ch > "\x80" &&
5111      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
5112  }
5113
5114  function isEmpty(obj) {
5115    for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5116    return true;
5117  }
5118
5119  var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5120
5121  // DOM UTILITIES
5122
5123  function elt(tag, content, className, style) {
5124    var e = document.createElement(tag);
5125    if (className) e.className = className;
5126    if (style) e.style.cssText = style;
5127    if (typeof content == "string") setTextContent(e, content);
5128    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5129    return e;
5130  }
5131
5132  function removeChildren(e) {
5133    // IE will break all parent-child relations in subnodes when setting innerHTML
5134    if (!ie) e.innerHTML = "";
5135    else while (e.firstChild) e.removeChild(e.firstChild);
5136    return e;
5137  }
5138
5139  function removeChildrenAndAdd(parent, e) {
5140    return removeChildren(parent).appendChild(e);
5141  }
5142
5143  function setTextContent(e, str) {
5144    if (ie_lt9) {
5145      e.innerHTML = "";
5146      e.appendChild(document.createTextNode(str));
5147    } else e.textContent = str;
5148  }
5149
5150  function getRect(node) {
5151    return node.getBoundingClientRect();
5152  }
5153  CodeMirror.replaceGetRect = function(f) { getRect = f; };
5154
5155  // FEATURE DETECTION
5156
5157  // Detect drag-and-drop
5158  var dragAndDrop = function() {
5159    // There is *some* kind of drag-and-drop support in IE6-8, but I
5160    // couldn't get it to work yet.
5161    if (ie_lt9) return false;
5162    var div = elt('div');
5163    return "draggable" in div || "dragDrop" in div;
5164  }();
5165
5166  // For a reason I have yet to figure out, some browsers disallow
5167  // word wrapping between certain characters *only* if a new inline
5168  // element is started between them. This makes it hard to reliably
5169  // measure the position of things, since that requires inserting an
5170  // extra span. This terribly fragile set of regexps matches the
5171  // character combinations that suffer from this phenomenon on the
5172  // various browsers.
5173  var spanAffectsWrapping = /^$/; // Won't match any two-character string
5174  if (gecko) spanAffectsWrapping = /$'/;
5175  else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
5176  else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
5177
5178  var knownScrollbarWidth;
5179  function scrollbarWidth(measure) {
5180    if (knownScrollbarWidth != null) return knownScrollbarWidth;
5181    var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
5182    removeChildrenAndAdd(measure, test);
5183    if (test.offsetWidth)
5184      knownScrollbarWidth = test.offsetHeight - test.clientHeight;
5185    return knownScrollbarWidth || 0;
5186  }
5187
5188  var zwspSupported;
5189  function zeroWidthElement(measure) {
5190    if (zwspSupported == null) {
5191      var test = elt("span", "\u200b");
5192      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5193      if (measure.firstChild.offsetHeight != 0)
5194        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
5195    }
5196    if (zwspSupported) return elt("span", "\u200b");
5197    else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5198  }
5199
5200  // See if "".split is the broken IE version, if so, provide an
5201  // alternative way to split lines.
5202  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5203    var pos = 0, result = [], l = string.length;
5204    while (pos <= l) {
5205      var nl = string.indexOf("\n", pos);
5206      if (nl == -1) nl = string.length;
5207      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
5208      var rt = line.indexOf("\r");
5209      if (rt != -1) {
5210        result.push(line.slice(0, rt));
5211        pos += rt + 1;
5212      } else {
5213        result.push(line);
5214        pos = nl + 1;
5215      }
5216    }
5217    return result;
5218  } : function(string){return string.split(/\r\n?|\n/);};
5219  CodeMirror.splitLines = splitLines;
5220
5221  var hasSelection = window.getSelection ? function(te) {
5222    try { return te.selectionStart != te.selectionEnd; }
5223    catch(e) { return false; }
5224  } : function(te) {
5225    try {var range = te.ownerDocument.selection.createRange();}
5226    catch(e) {}
5227    if (!range || range.parentElement() != te) return false;
5228    return range.compareEndPoints("StartToEnd", range) != 0;
5229  };
5230
5231  var hasCopyEvent = (function() {
5232    var e = elt("div");
5233    if ("oncopy" in e) return true;
5234    e.setAttribute("oncopy", "return;");
5235    return typeof e.oncopy == 'function';
5236  })();
5237
5238  // KEY NAMING
5239
5240  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5241                  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5242                  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5243                  46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
5244                  186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5245                  221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
5246                  63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
5247  CodeMirror.keyNames = keyNames;
5248  (function() {
5249    // Number keys
5250    for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
5251    // Alphabetic keys
5252    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5253    // Function keys
5254    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
5255  })();
5256
5257  // BIDI HELPERS
5258
5259  function iterateBidiSections(order, from, to, f) {
5260    if (!order) return f(from, to, "ltr");
5261    for (var i = 0; i < order.length; ++i) {
5262      var part = order[i];
5263      if (part.from < to && part.to > from || from == to && part.to == from)
5264        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5265    }
5266  }
5267
5268  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
5269  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
5270
5271  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
5272  function lineRight(line) {
5273    var order = getOrder(line);
5274    if (!order) return line.text.length;
5275    return bidiRight(lst(order));
5276  }
5277
5278  function lineStart(cm, lineN) {
5279    var line = getLine(cm.doc, lineN);
5280    var visual = visualLine(cm.doc, line);
5281    if (visual != line) lineN = lineNo(visual);
5282    var order = getOrder(visual);
5283    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5284    return Pos(lineN, ch);
5285  }
5286  function lineEnd(cm, lineN) {
5287    var merged, line;
5288    while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5289      lineN = merged.find().to.line;
5290    var order = getOrder(line);
5291    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5292    return Pos(lineN, ch);
5293  }
5294
5295  // This is somewhat involved. It is needed in order to move
5296  // 'visually' through bi-directional text -- i.e., pressing left
5297  // should make the cursor go left, even when in RTL text. The
5298  // tricky part is the 'jumps', where RTL and LTR text touch each
5299  // other. This often requires the cursor offset to move more than
5300  // one unit, in order to visually move one unit.
5301  function moveVisually(line, start, dir, byUnit) {
5302    var bidi = getOrder(line);
5303    if (!bidi) return moveLogically(line, start, dir, byUnit);
5304    var moveOneUnit = byUnit ? function(pos, dir) {
5305      do pos += dir;
5306      while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
5307      return pos;
5308    } : function(pos, dir) { return pos + dir; };
5309    var linedir = bidi[0].level;
5310    for (var i = 0; i < bidi.length; ++i) {
5311      var part = bidi[i], sticky = part.level % 2 == linedir;
5312      if ((part.from < start && part.to > start) ||
5313          (sticky && (part.from == start || part.to == start))) break;
5314    }
5315    var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
5316
5317    while (target != null) {
5318      if (part.level % 2 == linedir) {
5319        if (target < part.from || target > part.to) {
5320          part = bidi[i += dir];
5321          target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
5322        } else break;
5323      } else {
5324        if (target == bidiLeft(part)) {
5325          part = bidi[--i];
5326          target = part && bidiRight(part);
5327        } else if (target == bidiRight(part)) {
5328          part = bidi[++i];
5329          target = part && bidiLeft(part);
5330        } else break;
5331      }
5332    }
5333
5334    return target < 0 || target > line.text.length ? null : target;
5335  }
5336
5337  function moveLogically(line, start, dir, byUnit) {
5338    var target = start + dir;
5339    if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
5340    return target < 0 || target > line.text.length ? null : target;
5341  }
5342
5343  // Bidirectional ordering algorithm
5344  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
5345  // that this (partially) implements.
5346
5347  // One-char codes used for character types:
5348  // L (L):   Left-to-Right
5349  // R (R):   Right-to-Left
5350  // r (AL):  Right-to-Left Arabic
5351  // 1 (EN):  European Number
5352  // + (ES):  European Number Separator
5353  // % (ET):  European Number Terminator
5354  // n (AN):  Arabic Number
5355  // , (CS):  Common Number Separator
5356  // m (NSM): Non-Spacing Mark
5357  // b (BN):  Boundary Neutral
5358  // s (B):   Paragraph Separator
5359  // t (S):   Segment Separator
5360  // w (WS):  Whitespace
5361  // N (ON):  Other Neutrals
5362
5363  // Returns null if characters are ordered as they appear
5364  // (left-to-right), or an array of sections ({from, to, level}
5365  // objects) in the order in which they occur visually.
5366  var bidiOrdering = (function() {
5367    // Character types for codepoints 0 to 0xff
5368    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
5369    // Character types for codepoints 0x600 to 0x6ff
5370    var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
5371    function charType(code) {
5372      if (code <= 0xff) return lowTypes.charAt(code);
5373      else if (0x590 <= code && code <= 0x5f4) return "R";
5374      else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
5375      else if (0x700 <= code && code <= 0x8ac) return "r";
5376      else return "L";
5377    }
5378
5379    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5380    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5381    // Browsers seem to always treat the boundaries of block elements as being L.
5382    var outerType = "L";
5383
5384    return function(str) {
5385      if (!bidiRE.test(str)) return false;
5386      var len = str.length, types = [];
5387      for (var i = 0, type; i < len; ++i)
5388        types.push(type = charType(str.charCodeAt(i)));
5389
5390      // W1. Examine each non-spacing mark (NSM) in the level run, and
5391      // change the type of the NSM to the type of the previous
5392      // character. If the NSM is at the start of the level run, it will
5393      // get the type of sor.
5394      for (var i = 0, prev = outerType; i < len; ++i) {
5395        var type = types[i];
5396        if (type == "m") types[i] = prev;
5397        else prev = type;
5398      }
5399
5400      // W2. Search backwards from each instance of a European number
5401      // until the first strong type (R, L, AL, or sor) is found. If an
5402      // AL is found, change the type of the European number to Arabic
5403      // number.
5404      // W3. Change all ALs to R.
5405      for (var i = 0, cur = outerType; i < len; ++i) {
5406        var type = types[i];
5407        if (type == "1" && cur == "r") types[i] = "n";
5408        else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5409      }
5410
5411      // W4. A single European separator between two European numbers
5412      // changes to a European number. A single common separator between
5413      // two numbers of the same type changes to that type.
5414      for (var i = 1, prev = types[0]; i < len - 1; ++i) {
5415        var type = types[i];
5416        if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
5417        else if (type == "," && prev == types[i+1] &&
5418                 (prev == "1" || prev == "n")) types[i] = prev;
5419        prev = type;
5420      }
5421
5422      // W5. A sequence of European terminators adjacent to European
5423      // numbers changes to all European numbers.
5424      // W6. Otherwise, separators and terminators change to Other
5425      // Neutral.
5426      for (var i = 0; i < len; ++i) {
5427        var type = types[i];
5428        if (type == ",") types[i] = "N";
5429        else if (type == "%") {
5430          for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5431          var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
5432          for (var j = i; j < end; ++j) types[j] = replace;
5433          i = end - 1;
5434        }
5435      }
5436
5437      // W7. Search backwards from each instance of a European number
5438      // until the first strong type (R, L, or sor) is found. If an L is
5439      // found, then change the type of the European number to L.
5440      for (var i = 0, cur = outerType; i < len; ++i) {
5441        var type = types[i];
5442        if (cur == "L" && type == "1") types[i] = "L";
5443        else if (isStrong.test(type)) cur = type;
5444      }
5445
5446      // N1. A sequence of neutrals takes the direction of the
5447      // surrounding strong text if the text on both sides has the same
5448      // direction. European and Arabic numbers act as if they were R in
5449      // terms of their influence on neutrals. Start-of-level-run (sor)
5450      // and end-of-level-run (eor) are used at level run boundaries.
5451      // N2. Any remaining neutrals take the embedding direction.
5452      for (var i = 0; i < len; ++i) {
5453        if (isNeutral.test(types[i])) {
5454          for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5455          var before = (i ? types[i-1] : outerType) == "L";
5456          var after = (end < len - 1 ? types[end] : outerType) == "L";
5457          var replace = before || after ? "L" : "R";
5458          for (var j = i; j < end; ++j) types[j] = replace;
5459          i = end - 1;
5460        }
5461      }
5462
5463      // Here we depart from the documented algorithm, in order to avoid
5464      // building up an actual levels array. Since there are only three
5465      // levels (0, 1, 2) in an implementation that doesn't take
5466      // explicit embedding into account, we can build up the order on
5467      // the fly, without following the level-based algorithm.
5468      var order = [], m;
5469      for (var i = 0; i < len;) {
5470        if (countsAsLeft.test(types[i])) {
5471          var start = i;
5472          for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5473          order.push({from: start, to: i, level: 0});
5474        } else {
5475          var pos = i, at = order.length;
5476          for (++i; i < len && types[i] != "L"; ++i) {}
5477          for (var j = pos; j < i;) {
5478            if (countsAsNum.test(types[j])) {
5479              if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
5480              var nstart = j;
5481              for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5482              order.splice(at, 0, {from: nstart, to: j, level: 2});
5483              pos = j;
5484            } else ++j;
5485          }
5486          if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
5487        }
5488      }
5489      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5490        order[0].from = m[0].length;
5491        order.unshift({from: 0, to: m[0].length, level: 0});
5492      }
5493      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5494        lst(order).to -= m[0].length;
5495        order.push({from: len - m[0].length, to: len, level: 0});
5496      }
5497      if (order[0].level != lst(order).level)
5498        order.push({from: len, to: len, level: order[0].level});
5499
5500      return order;
5501    };
5502  })();
5503
5504  // THE END
5505
5506  CodeMirror.version = "3.1 +";
5507
5508  return CodeMirror;
5509})();
5510