81771e99b12aaaddeff6e425e294ed359e9ab77a
[WebKit-https.git] / Source / WebInspectorUI / Tools / PrettyPrinting / codemirror.js
1 // CodeMirror is the only global var we claim
2 window.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 = /win/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   if (opera_version && opera_version >= 15) { opera = false; webkit = true; }
32   // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
33   var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
34   var captureMiddleClick = gecko || (ie && !ie_lt9);
35
36   // Optimize some code when these features are not used
37   var sawReadOnlySpans = false, sawCollapsedSpans = false;
38
39   // CONSTRUCTOR
40
41   function CodeMirror(place, options) {
42     if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
43
44     this.options = options = options || {};
45     // Determine effective options based on given values and defaults.
46     for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
47       options[opt] = defaults[opt];
48     setGuttersForLineNumbers(options);
49
50     var docStart = typeof options.value == "string" ? 0 : options.value.first;
51     var display = this.display = makeDisplay(place, docStart);
52     display.wrapper.CodeMirror = this;
53     updateGutters(this);
54     if (options.autofocus && !mobile) focusInput(this);
55
56     this.state = {keyMaps: [],
57                   overlays: [],
58                   modeGen: 0,
59                   overwrite: false, focused: false,
60                   suppressEdits: false, pasteIncoming: false,
61                   draggingText: false,
62                   highlight: new Delayed()};
63
64     themeChanged(this);
65     if (options.lineWrapping)
66       this.display.wrapper.className += " CodeMirror-wrap";
67
68     var doc = options.value;
69     if (typeof doc == "string") doc = new Doc(options.value, options.mode);
70     operation(this, attachDoc)(this, doc);
71
72     // Override magic textarea content restore that IE sometimes does
73     // on our hidden textarea on reload
74     if (ie) setTimeout(bind(resetInput, this, true), 20);
75
76     registerEventHandlers(this);
77     // IE throws unspecified error in certain cases, when
78     // trying to access activeElement before onload
79     var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
80     if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
81     else onBlur(this);
82
83     operation(this, function() {
84       for (var opt in optionHandlers)
85         if (optionHandlers.propertyIsEnumerable(opt))
86           optionHandlers[opt](this, options[opt], Init);
87       for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
88     })();
89   }
90
91   // DISPLAY CONSTRUCTOR
92
93   function makeDisplay(place, docStart) {
94     var d = {};
95
96     var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
97     if (webkit) input.style.width = "1000px";
98     else input.setAttribute("wrap", "off");
99     // if border: 0; -- iOS fails to open keyboard (issue #1287)
100     if (ios) input.style.border = "1px solid black";
101     input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
102
103     // Wraps and hides input textarea
104     d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
105     // The actual fake scrollbars.
106     d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
107     d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
108     d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
109     d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
110     // DIVs containing the selection and the actual code
111     d.lineDiv = elt("div", null, "CodeMirror-code");
112     d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
113     // Blinky cursor, and element used to ensure cursor fits at the end of a line
114     d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
115     // Secondary cursor, shown when on a 'jump' in bi-directional text
116     d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
117     // Used to measure text size
118     d.measure = elt("div", null, "CodeMirror-measure");
119     // Wraps everything that needs to exist inside the vertically-padded coordinate system
120     d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
121                          null, "position: relative; outline: none");
122     // Moved around its parent to cover visible view
123     d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
124     // Set to the height of the text, causes scrolling
125     d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
126     // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
127     d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
128     // Will contain the gutters, if any
129     d.gutters = elt("div", null, "CodeMirror-gutters");
130     d.lineGutter = null;
131     // Provides scrolling
132     d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "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.gutterFiller, 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
168     d.cachedCharWidth = d.cachedTextHeight = null;
169     d.measureLineCache = [];
170     d.measureLineCachePos = 0;
171
172     // Tracks when resetInput has punted to just putting a short
173     // string instead of the (large) selection.
174     d.inaccurateSelection = false;
175
176     // Tracks the maximum line length so that the horizontal scrollbar
177     // can be kept static when scrolling.
178     d.maxLine = null;
179     d.maxLineLength = 0;
180     d.maxLineChanged = false;
181
182     // Used for measuring wheel scrolling granularity
183     d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
184
185     return d;
186   }
187
188   // STATE UPDATES
189
190   // Used to get the editor into a consistent state again when options change.
191
192   function loadMode(cm) {
193     cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
194     cm.doc.iter(function(line) {
195       if (line.stateAfter) line.stateAfter = null;
196       if (line.styles) line.styles = null;
197     });
198     cm.doc.frontier = cm.doc.first;
199     startWorker(cm, 100);
200     cm.state.modeGen++;
201     if (cm.curOp) regChange(cm);
202   }
203
204   function wrappingChanged(cm) {
205     if (cm.options.lineWrapping) {
206       cm.display.wrapper.className += " CodeMirror-wrap";
207       cm.display.sizer.style.minWidth = "";
208     } else {
209       cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
210       computeMaxLength(cm);
211     }
212     estimateLineHeights(cm);
213     regChange(cm);
214     clearCaches(cm);
215     setTimeout(function(){updateScrollbars(cm);}, 100);
216   }
217
218   function estimateHeight(cm) {
219     var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
220     var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
221     return function(line) {
222       if (lineIsHidden(cm.doc, line))
223         return 0;
224       else if (wrapping)
225         return (Math.ceil(line.text.length / perLine) || 1) * th;
226       else
227         return th;
228     };
229   }
230
231   function estimateLineHeights(cm) {
232     var doc = cm.doc, est = estimateHeight(cm);
233     doc.iter(function(line) {
234       var estHeight = est(line);
235       if (estHeight != line.height) updateLineHeight(line, estHeight);
236     });
237   }
238
239   function keyMapChanged(cm) {
240     var map = keyMap[cm.options.keyMap], style = map.style;
241     cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
242       (style ? " cm-keymap-" + style : "");
243     cm.state.disableInput = map.disableInput;
244   }
245
246   function themeChanged(cm) {
247     cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
248       cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
249     clearCaches(cm);
250   }
251
252   function guttersChanged(cm) {
253     updateGutters(cm);
254     regChange(cm);
255     setTimeout(function(){alignHorizontally(cm);}, 20);
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 = indexOf(options.gutters, "CodeMirror-linenumbers");
308     if (found == -1 && options.lineNumbers) {
309       options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
310     } else if (found > -1 && !options.lineNumbers) {
311       options.gutters = options.gutters.slice(0);
312       options.gutters.splice(i, 1);
313     }
314   }
315
316   // SCROLLBARS
317
318   // Re-synchronize the fake scrollbars with the actual size of the
319   // content. Optionally force a scrollTop.
320   function updateScrollbars(cm) {
321     var d = cm.display, docHeight = cm.doc.height;
322     var totalHeight = docHeight + paddingVert(d);
323     d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
324     d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
325     var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
326     var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
327     var needsV = scrollHeight > (d.scroller.clientHeight + 1);
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 {
334       d.scrollbarV.style.display = "";
335       d.scrollbarV.firstChild.style.height = "0";
336     }
337     if (needsH) {
338       d.scrollbarH.style.display = "block";
339       d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
340       d.scrollbarH.firstChild.style.width =
341         (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
342     } else {
343       d.scrollbarH.style.display = "";
344       d.scrollbarH.firstChild.style.width = "0";
345     }
346     if (needsH && needsV) {
347       d.scrollbarFiller.style.display = "block";
348       d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
349     } else d.scrollbarFiller.style.display = "";
350     if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
351       d.gutterFiller.style.display = "block";
352       d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
353       d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
354     } else d.gutterFiller.style.display = "";
355
356     if (mac_geLion && scrollbarWidth(d.measure) === 0)
357       d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
358   }
359
360   function visibleLines(display, doc, viewPort) {
361     var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
362     if (typeof viewPort == "number") top = viewPort;
363     else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
364     top = Math.floor(top - paddingTop(display));
365     var bottom = Math.ceil(top + height);
366     return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
367   }
368
369   // LINE NUMBERS
370
371   function alignHorizontally(cm) {
372     var display = cm.display;
373     if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
374     var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
375     var gutterW = display.gutters.offsetWidth, l = comp + "px";
376     for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
377       for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
378     }
379     if (cm.options.fixedGutter)
380       display.gutters.style.left = (comp + gutterW) + "px";
381   }
382
383   function maybeUpdateLineNumberWidth(cm) {
384     if (!cm.options.lineNumbers) return false;
385     var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
386     if (last.length != display.lineNumChars) {
387       var test = display.measure.appendChild(elt("div", [elt("div", last)],
388                                                  "CodeMirror-linenumber CodeMirror-gutter-elt"));
389       var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
390       display.lineGutter.style.width = "";
391       display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
392       display.lineNumWidth = display.lineNumInnerWidth + padding;
393       display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
394       display.lineGutter.style.width = display.lineNumWidth + "px";
395       return true;
396     }
397     return false;
398   }
399
400   function lineNumberFor(options, i) {
401     return String(options.lineNumberFormatter(i + options.firstLineNumber));
402   }
403   function compensateForHScroll(display) {
404     return getRect(display.scroller).left - getRect(display.sizer).left;
405   }
406
407   // DISPLAY DRAWING
408
409   function updateDisplay(cm, changes, viewPort, forced) {
410     var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
411     var visible = visibleLines(cm.display, cm.doc, viewPort);
412     for (;;) {
413       var oldWidth = cm.display.scroller.clientWidth;
414       if (!updateDisplayInner(cm, changes, visible, forced)) break;
415       updated = true;
416       changes = [];
417       updateSelection(cm);
418       updateScrollbars(cm);
419       if (cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
420         forced = true;
421         continue;
422       }
423       forced = false;
424
425       // Clip forced viewport to actual scrollable area
426       if (viewPort)
427         viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
428                             typeof viewPort == "number" ? viewPort : viewPort.top);
429       visible = visibleLines(cm.display, cm.doc, viewPort);
430       if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
431         break;
432     }
433
434     if (updated) {
435       signalLater(cm, "update", cm);
436       if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
437         signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
438     }
439     return updated;
440   }
441
442   // Uses a set of changes plus the current scroll position to
443   // determine which DOM updates have to be made, and makes the
444   // updates.
445   function updateDisplayInner(cm, changes, visible, forced) {
446     var display = cm.display, doc = cm.doc;
447     if (!display.wrapper.clientWidth) {
448       display.showingFrom = display.showingTo = doc.first;
449       display.viewOffset = 0;
450       return;
451     }
452
453     // Bail out if the visible area is already rendered and nothing changed.
454     if (!forced && changes.length == 0 &&
455         visible.from > display.showingFrom && visible.to < display.showingTo)
456       return;
457
458     if (maybeUpdateLineNumberWidth(cm))
459       changes = [{from: doc.first, to: doc.first + doc.size}];
460     var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
461     display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
462
463     // Used to determine which lines need their line numbers updated
464     var positionsChangedFrom = Infinity;
465     if (cm.options.lineNumbers)
466       for (var i = 0; i < changes.length; ++i)
467         if (changes[i].diff && changes[i].from < positionsChangedFrom) { positionsChangedFrom = changes[i].from; }
468
469     var end = doc.first + doc.size;
470     var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
471     var to = Math.min(end, visible.to + cm.options.viewportMargin);
472     if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
473     if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
474     if (sawCollapsedSpans) {
475       from = lineNo(visualLine(doc, getLine(doc, from)));
476       while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
477     }
478
479     // Create a range of theoretically intact lines, and punch holes
480     // in that using the change info.
481     var intact = [{from: Math.max(display.showingFrom, doc.first),
482                    to: Math.min(display.showingTo, end)}];
483     if (intact[0].from >= intact[0].to) intact = [];
484     else intact = computeIntact(intact, changes);
485     // When merged lines are present, we might have to reduce the
486     // intact ranges because changes in continued fragments of the
487     // intact lines do require the lines to be redrawn.
488     if (sawCollapsedSpans)
489       for (var i = 0; i < intact.length; ++i) {
490         var range = intact[i], merged;
491         while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
492           var newTo = merged.find().from.line;
493           if (newTo > range.from) range.to = newTo;
494           else { intact.splice(i--, 1); break; }
495         }
496       }
497
498     // Clip off the parts that won't be visible
499     var intactLines = 0;
500     for (var i = 0; i < intact.length; ++i) {
501       var range = intact[i];
502       if (range.from < from) range.from = from;
503       if (range.to > to) range.to = to;
504       if (range.from >= range.to) intact.splice(i--, 1);
505       else intactLines += range.to - range.from;
506     }
507     if (!forced && intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
508       updateViewOffset(cm);
509       return;
510     }
511     intact.sort(function(a, b) {return a.from - b.from;});
512
513     // Avoid crashing on IE's "unspecified error" when in iframes
514     try {
515       var focused = document.activeElement;
516     } catch(e) {}
517     if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
518     patchDisplay(cm, from, to, intact, positionsChangedFrom);
519     display.lineDiv.style.display = "";
520     if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
521
522     var different = from != display.showingFrom || to != display.showingTo ||
523       display.lastSizeC != display.wrapper.clientHeight;
524     // This is just a bogus formula that detects when the editor is
525     // resized or the font size changes.
526     if (different) {
527       display.lastSizeC = display.wrapper.clientHeight;
528       startWorker(cm, 400);
529     }
530     display.showingFrom = from; display.showingTo = to;
531
532     updateHeightsInViewport(cm);
533     updateViewOffset(cm);
534
535     return true;
536   }
537
538   function updateHeightsInViewport(cm) {
539     var display = cm.display;
540     var prevBottom = display.lineDiv.offsetTop;
541     for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
542       if (ie_lt8) {
543         var bot = node.offsetTop + node.offsetHeight;
544         height = bot - prevBottom;
545         prevBottom = bot;
546       } else {
547         var box = getRect(node);
548         height = box.bottom - box.top;
549       }
550       var diff = node.lineObj.height - height;
551       if (height < 2) height = textHeight(display);
552       if (diff > .001 || diff < -.001) {
553         updateLineHeight(node.lineObj, height);
554         var widgets = node.lineObj.widgets;
555         if (widgets) for (var i = 0; i < widgets.length; ++i)
556           widgets[i].height = widgets[i].node.offsetHeight;
557       }
558     }
559   }
560
561   function updateViewOffset(cm) {
562     var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
563     // Position the mover div to align with the current virtual scroll position
564     cm.display.mover.style.top = off + "px";
565   }
566
567   function computeIntact(intact, changes) {
568     for (var i = 0, l = changes.length || 0; i < l; ++i) {
569       var change = changes[i], intact2 = [], diff = change.diff || 0;
570       for (var j = 0, l2 = intact.length; j < l2; ++j) {
571         var range = intact[j];
572         if (change.to <= range.from && change.diff) {
573           intact2.push({from: range.from + diff, to: range.to + diff});
574         } else if (change.to <= range.from || change.from >= range.to) {
575           intact2.push(range);
576         } else {
577           if (change.from > range.from)
578             intact2.push({from: range.from, to: change.from});
579           if (change.to < range.to)
580             intact2.push({from: change.to + diff, to: range.to + diff});
581         }
582       }
583       intact = intact2;
584     }
585     return intact;
586   }
587
588   function getDimensions(cm) {
589     var d = cm.display, left = {}, width = {};
590     for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
591       left[cm.options.gutters[i]] = n.offsetLeft;
592       width[cm.options.gutters[i]] = n.offsetWidth;
593     }
594     return {fixedPos: compensateForHScroll(d),
595             gutterTotalWidth: d.gutters.offsetWidth,
596             gutterLeft: left,
597             gutterWidth: width,
598             wrapperWidth: d.wrapper.clientWidth};
599   }
600
601   function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
602     var dims = getDimensions(cm);
603     var display = cm.display, lineNumbers = cm.options.lineNumbers;
604     if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
605       removeChildren(display.lineDiv);
606     var container = display.lineDiv, cur = container.firstChild;
607
608     function rm(node) {
609       var next = node.nextSibling;
610       if (webkit && mac && cm.display.currentWheelTarget == node) {
611         node.style.display = "none";
612         node.lineObj = null;
613       } else {
614         node.parentNode.removeChild(node);
615       }
616       return next;
617     }
618
619     var nextIntact = intact.shift(), lineN = from;
620     cm.doc.iter(from, to, function(line) {
621       if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
622       if (lineIsHidden(cm.doc, line)) {
623         if (line.height != 0) updateLineHeight(line, 0);
624         if (line.widgets && cur && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
625           var w = line.widgets[i];
626           if (w.showIfHidden) {
627             var prev = cur.previousSibling;
628             if (/pre/i.test(prev.nodeName)) {
629               var wrap = elt("div", null, null, "position: relative");
630               prev.parentNode.replaceChild(wrap, prev);
631               wrap.appendChild(prev);
632               prev = wrap;
633             }
634             var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
635             if (!w.handleMouseEvents) wnode.ignoreEvents = true;
636             positionLineWidget(w, wnode, prev, dims);
637           }
638         }
639       } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
640         // This line is intact. Skip to the actual node. Update its
641         // line number if needed.
642         while (cur.lineObj != line) cur = rm(cur);
643         if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
644           setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
645         cur = cur.nextSibling;
646       } else {
647         // For lines with widgets, make an attempt to find and reuse
648         // the existing element, so that widgets aren't needlessly
649         // removed and re-inserted into the dom
650         if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
651           if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
652         // This line needs to be generated.
653         var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
654         if (lineNode != reuse) {
655           container.insertBefore(lineNode, cur);
656         } else {
657           while (cur != reuse) cur = rm(cur);
658           cur = cur.nextSibling;
659         }
660
661         lineNode.lineObj = line;
662       }
663       ++lineN;
664     });
665     while (cur) cur = rm(cur);
666   }
667
668   function buildLineElement(cm, line, lineNo, dims, reuse) {
669     var built = buildLineContent(cm, line), lineElement = built.pre;
670     var markers = line.gutterMarkers, display = cm.display, wrap;
671
672     var bgClass = built.bgClass ? built.bgClass + " " + (line.bgClass || "") : line.bgClass;
673     if (!cm.options.lineNumbers && !markers && !bgClass && !line.wrapClass && !line.widgets)
674       return lineElement;
675
676     // Lines with gutter elements, widgets or a background class need
677     // to be wrapped again, and have the extra elements added to the
678     // wrapper div
679
680     if (reuse) {
681       reuse.alignable = null;
682       var isOk = true, widgetsSeen = 0, insertBefore = null;
683       for (var n = reuse.firstChild, next; n; n = next) {
684         next = n.nextSibling;
685         if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
686           reuse.removeChild(n);
687         } else {
688           for (var i = 0; i < line.widgets.length; ++i) {
689             var widget = line.widgets[i];
690             if (widget.node == n.firstChild) {
691               if (!widget.above && !insertBefore) insertBefore = n;
692               positionLineWidget(widget, n, reuse, dims);
693               ++widgetsSeen;
694               break;
695             }
696           }
697           if (i == line.widgets.length) { isOk = false; break; }
698         }
699       }
700       reuse.insertBefore(lineElement, insertBefore);
701       if (isOk && widgetsSeen == line.widgets.length) {
702         wrap = reuse;
703         reuse.className = line.wrapClass || "";
704       }
705     }
706     if (!wrap) {
707       wrap = elt("div", null, line.wrapClass, "position: relative");
708       wrap.appendChild(lineElement);
709     }
710     // Kludge to make sure the styled element lies behind the selection (by z-index)
711     if (bgClass)
712       wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild);
713     if (cm.options.lineNumbers || markers) {
714       var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
715                                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
716                                          wrap.firstChild);
717       if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
718       if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
719         wrap.lineNumber = gutterWrap.appendChild(
720           elt("div", lineNumberFor(cm.options, lineNo),
721               "CodeMirror-linenumber CodeMirror-gutter-elt",
722               "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
723               + display.lineNumInnerWidth + "px"));
724       if (markers)
725         for (var k = 0; k < cm.options.gutters.length; ++k) {
726           var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
727           if (found)
728             gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
729                                        dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
730         }
731     }
732     if (ie_lt8) wrap.style.zIndex = 2;
733     if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
734       var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
735       if (!widget.handleMouseEvents) node.ignoreEvents = true;
736       positionLineWidget(widget, node, wrap, dims);
737       if (widget.above)
738         wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
739       else
740         wrap.appendChild(node);
741       signalLater(widget, "redraw");
742     }
743     return wrap;
744   }
745
746   function positionLineWidget(widget, node, wrap, dims) {
747     if (widget.noHScroll) {
748       (wrap.alignable || (wrap.alignable = [])).push(node);
749       var width = dims.wrapperWidth;
750       node.style.left = dims.fixedPos + "px";
751       if (!widget.coverGutter) {
752         width -= dims.gutterTotalWidth;
753         node.style.paddingLeft = dims.gutterTotalWidth + "px";
754       }
755       node.style.width = width + "px";
756     }
757     if (widget.coverGutter) {
758       node.style.zIndex = 5;
759       node.style.position = "relative";
760       if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
761     }
762   }
763
764   // SELECTION / CURSOR
765
766   function updateSelection(cm) {
767     var display = cm.display;
768     var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
769     if (collapsed || cm.options.showCursorWhenSelecting)
770       updateSelectionCursor(cm);
771     else
772       display.cursor.style.display = display.otherCursor.style.display = "none";
773     if (!collapsed)
774       updateSelectionRange(cm);
775     else
776       display.selectionDiv.style.display = "none";
777
778     // Move the hidden textarea near the cursor to prevent scrolling artifacts
779     if (cm.options.moveInputWithCursor) {
780       var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
781       var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
782       display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
783                                                         headPos.top + lineOff.top - wrapOff.top)) + "px";
784       display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
785                                                          headPos.left + lineOff.left - wrapOff.left)) + "px";
786     }
787   }
788
789   // No selection, plain cursor
790   function updateSelectionCursor(cm) {
791     var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
792     display.cursor.style.left = pos.left + "px";
793     display.cursor.style.top = pos.top + "px";
794     display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
795     display.cursor.style.display = "";
796
797     if (pos.other) {
798       display.otherCursor.style.display = "";
799       display.otherCursor.style.left = pos.other.left + "px";
800       display.otherCursor.style.top = pos.other.top + "px";
801       display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
802     } else { display.otherCursor.style.display = "none"; }
803   }
804
805   // Highlight selection
806   function updateSelectionRange(cm) {
807     var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
808     var fragment = document.createDocumentFragment();
809     var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
810
811     function add(left, top, width, bottom) {
812       if (top < 0) top = 0;
813       fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
814                                "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
815                                "px; height: " + (bottom - top) + "px"));
816     }
817
818     function drawForLine(line, fromArg, toArg) {
819       var lineObj = getLine(doc, line);
820       var lineLen = lineObj.text.length;
821       var start, end;
822       function coords(ch, bias) {
823         return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
824       }
825
826       iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
827         var leftPos = coords(from, "left"), rightPos, left, right;
828         if (from == to) {
829           rightPos = leftPos;
830           left = right = leftPos.left;
831         } else {
832           rightPos = coords(to - 1, "right");
833           if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
834           left = leftPos.left;
835           right = rightPos.right;
836         }
837         if (fromArg == null && from == 0) left = pl;
838         if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
839           add(left, leftPos.top, null, leftPos.bottom);
840           left = pl;
841           if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
842         }
843         if (toArg == null && to == lineLen) right = clientWidth;
844         if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
845           start = leftPos;
846         if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
847           end = rightPos;
848         if (left < pl + 1) left = pl;
849         add(left, rightPos.top, right - left, rightPos.bottom);
850       });
851       return {start: start, end: end};
852     }
853
854     if (sel.from.line == sel.to.line) {
855       drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
856     } else {
857       var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
858       var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
859       var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
860       var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
861       if (singleVLine) {
862         if (leftEnd.top < rightStart.top - 2) {
863           add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
864           add(pl, rightStart.top, rightStart.left, rightStart.bottom);
865         } else {
866           add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
867         }
868       }
869       if (leftEnd.bottom < rightStart.top)
870         add(pl, leftEnd.bottom, null, rightStart.top);
871     }
872
873     removeChildrenAndAdd(display.selectionDiv, fragment);
874     display.selectionDiv.style.display = "";
875   }
876
877   // Cursor-blinking
878   function restartBlink(cm) {
879     if (!cm.state.focused) return;
880     var display = cm.display;
881     clearInterval(display.blinker);
882     var on = true;
883     display.cursor.style.visibility = display.otherCursor.style.visibility = "";
884     if (cm.options.cursorBlinkRate > 0)
885       display.blinker = setInterval(function() {
886         display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
887       }, cm.options.cursorBlinkRate);
888   }
889
890   // HIGHLIGHT WORKER
891
892   function startWorker(cm, time) {
893     if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
894       cm.state.highlight.set(time, bind(highlightWorker, cm));
895   }
896
897   function highlightWorker(cm) {
898     var doc = cm.doc;
899     if (doc.frontier < doc.first) doc.frontier = doc.first;
900     if (doc.frontier >= cm.display.showingTo) return;
901     var end = +new Date + cm.options.workTime;
902     var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
903     var changed = [], prevChange;
904     doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
905       if (doc.frontier >= cm.display.showingFrom) { // Visible
906         var oldStyles = line.styles;
907         line.styles = highlightLine(cm, line, state);
908         var ischange = !oldStyles || oldStyles.length != line.styles.length;
909         for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
910         if (ischange) {
911           if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
912           else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
913         }
914         line.stateAfter = copyState(doc.mode, state);
915       } else {
916         processLine(cm, line, state);
917         line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
918       }
919       ++doc.frontier;
920       if (+new Date > end) {
921         startWorker(cm, cm.options.workDelay);
922         return true;
923       }
924     });
925     if (changed.length)
926       operation(cm, function() {
927         for (var i = 0; i < changed.length; ++i)
928           regChange(this, changed[i].start, changed[i].end);
929       })();
930   }
931
932   // Finds the line to start with when starting a parse. Tries to
933   // find a line with a stateAfter, so that it can start with a
934   // valid state. If that fails, it returns the line with the
935   // smallest indentation, which tends to need the least context to
936   // parse correctly.
937   function findStartLine(cm, n, precise) {
938     var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100;
939     for (var search = n, lim = n - maxScan; search > lim; --search) {
940       if (search <= doc.first) return doc.first;
941       var line = getLine(doc, search - 1);
942       if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
943       var indented = countColumn(line.text, null, cm.options.tabSize);
944       if (minline == null || minindent > indented) {
945         minline = search - 1;
946         minindent = indented;
947       }
948     }
949     return minline;
950   }
951
952   function getStateBefore(cm, n, precise) {
953     var doc = cm.doc, display = cm.display;
954     if (!doc.mode.startState) return true;
955     var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
956     if (!state) state = startState(doc.mode);
957     else state = copyState(doc.mode, state);
958     doc.iter(pos, n, function(line) {
959       processLine(cm, line, state);
960       var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
961       line.stateAfter = save ? copyState(doc.mode, state) : null;
962       ++pos;
963     });
964     return state;
965   }
966
967   // POSITION MEASUREMENT
968
969   function paddingTop(display) {return display.lineSpace.offsetTop;}
970   function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
971   function paddingLeft(display) {
972     var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
973     return e.offsetLeft;
974   }
975
976   function measureChar(cm, line, ch, data, bias) {
977     var dir = -1;
978     data = data || measureLine(cm, line);
979     if (data.crude) {
980       var left = data.left + ch * data.width;
981       return {left: left, right: left + data.width, top: data.top, bottom: data.bottom};
982     }
983
984     for (var pos = ch;; pos += dir) {
985       var r = data[pos];
986       if (r) break;
987       if (dir < 0 && pos == 0) dir = 1;
988     }
989     bias = pos > ch ? "left" : pos < ch ? "right" : bias;
990     if (bias == "left" && r.leftSide) r = r.leftSide;
991     else if (bias == "right" && r.rightSide) r = r.rightSide;
992     return {left: pos < ch ? r.right : r.left,
993             right: pos > ch ? r.left : r.right,
994             top: r.top,
995             bottom: r.bottom};
996   }
997
998   function findCachedMeasurement(cm, line) {
999     var cache = cm.display.measureLineCache;
1000     for (var i = 0; i < cache.length; ++i) {
1001       var memo = cache[i];
1002       if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
1003           cm.display.scroller.clientWidth == memo.width &&
1004           memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
1005         return memo;
1006     }
1007   }
1008
1009   function clearCachedMeasurement(cm, line) {
1010     var exists = findCachedMeasurement(cm, line);
1011     if (exists) exists.text = exists.measure = exists.markedSpans = null;
1012   }
1013
1014   function measureLine(cm, line) {
1015     // First look in the cache
1016     var cached = findCachedMeasurement(cm, line);
1017     if (cached) return cached.measure;
1018
1019     // Failing that, recompute and store result in cache
1020     var measure = measureLineInner(cm, line);
1021     var cache = cm.display.measureLineCache;
1022     var memo = {text: line.text, width: cm.display.scroller.clientWidth,
1023                 markedSpans: line.markedSpans, measure: measure,
1024                 classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
1025     if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
1026     else cache.push(memo);
1027     return measure;
1028   }
1029
1030   function measureLineInner(cm, line) {
1031     if (!cm.options.lineWrapping && line.text.length >= cm.options.crudeMeasuringFrom)
1032       return crudelyMeasureLine(cm, line);
1033
1034     var display = cm.display, measure = emptyArray(line.text.length);
1035     var pre = buildLineContent(cm, line, measure, true).pre;
1036
1037     // IE does not cache element positions of inline elements between
1038     // calls to getBoundingClientRect. This makes the loop below,
1039     // which gathers the positions of all the characters on the line,
1040     // do an amount of layout work quadratic to the number of
1041     // characters. When line wrapping is off, we try to improve things
1042     // by first subdividing the line into a bunch of inline blocks, so
1043     // that IE can reuse most of the layout information from caches
1044     // for those blocks. This does interfere with line wrapping, so it
1045     // doesn't work when wrapping is on, but in that case the
1046     // situation is slightly better, since IE does cache line-wrapping
1047     // information and only recomputes per-line.
1048     if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1049       var fragment = document.createDocumentFragment();
1050       var chunk = 10, n = pre.childNodes.length;
1051       for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1052         var wrap = elt("div", null, null, "display: inline-block");
1053         for (var j = 0; j < chunk && n; ++j) {
1054           wrap.appendChild(pre.firstChild);
1055           --n;
1056         }
1057         fragment.appendChild(wrap);
1058       }
1059       pre.appendChild(fragment);
1060     }
1061
1062     removeChildrenAndAdd(display.measure, pre);
1063
1064     var outer = getRect(display.lineDiv);
1065     var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1066     // Work around an IE7/8 bug where it will sometimes have randomly
1067     // replaced our pre with a clone at this point.
1068     if (ie_lt9 && display.measure.first != pre)
1069       removeChildrenAndAdd(display.measure, pre);
1070
1071     function measureRect(rect) {
1072       var top = rect.top - outer.top, bot = rect.bottom - outer.top;
1073       if (bot > maxBot) bot = maxBot;
1074       if (top < 0) top = 0;
1075       for (var i = vranges.length - 2; i >= 0; i -= 2) {
1076         var rtop = vranges[i], rbot = vranges[i+1];
1077         if (rtop > bot || rbot < top) continue;
1078         if (rtop <= top && rbot >= bot ||
1079             top <= rtop && bot >= rbot ||
1080             Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1081           vranges[i] = Math.min(top, rtop);
1082           vranges[i+1] = Math.max(bot, rbot);
1083           break;
1084         }
1085       }
1086       if (i < 0) { i = vranges.length; vranges.push(top, bot); }
1087       return {left: rect.left - outer.left,
1088               right: rect.right - outer.left,
1089               top: i, bottom: null};
1090     }
1091     function finishRect(rect) {
1092       rect.bottom = vranges[rect.top+1];
1093       rect.top = vranges[rect.top];
1094     }
1095
1096     for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1097       var node = cur, rect = null;
1098       // A widget might wrap, needs special care
1099       if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
1100         if (cur.firstChild.nodeType == 1) node = cur.firstChild;
1101         var rects = node.getClientRects();
1102         if (rects.length > 1) {
1103           rect = data[i] = measureRect(rects[0]);
1104           rect.rightSide = measureRect(rects[rects.length - 1]);
1105         }
1106       }
1107       if (!rect) rect = data[i] = measureRect(getRect(node));
1108       if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
1109       if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
1110     }
1111     removeChildren(cm.display.measure);
1112     for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1113       finishRect(cur);
1114       if (cur.leftSide) finishRect(cur.leftSide);
1115       if (cur.rightSide) finishRect(cur.rightSide);
1116     }
1117     return data;
1118   }
1119
1120   function crudelyMeasureLine(cm, line) {
1121     var copy = new Line(line.text.slice(0, 100), null);
1122     if (line.textClass) copy.textClass = line.textClass;
1123     var measure = measureLineInner(cm, copy);
1124     var left = measureChar(cm, copy, 0, measure, "left");
1125     var right = measureChar(cm, copy, 99, measure, "right");
1126     return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100};
1127   }
1128
1129   function measureLineWidth(cm, line) {
1130     var hasBadSpan = false;
1131     if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1132       var sp = line.markedSpans[i];
1133       if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1134     }
1135     var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1136     if (cached || line.text.length >= cm.options.crudeMeasuringFrom)
1137       return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right;
1138
1139     var pre = buildLineContent(cm, line, null, true).pre;
1140     var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1141     removeChildrenAndAdd(cm.display.measure, pre);
1142     return getRect(end).right - getRect(cm.display.lineDiv).left;
1143   }
1144
1145   function clearCaches(cm) {
1146     cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1147     cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1148     if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
1149     cm.display.lineNumChars = null;
1150   }
1151
1152   function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
1153   function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
1154
1155   // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1156   function intoCoordSystem(cm, lineObj, rect, context) {
1157     if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1158       var size = widgetHeight(lineObj.widgets[i]);
1159       rect.top += size; rect.bottom += size;
1160     }
1161     if (context == "line") return rect;
1162     if (!context) context = "local";
1163     var yOff = heightAtLine(cm, lineObj);
1164     if (context == "local") yOff += paddingTop(cm.display);
1165     else yOff -= cm.display.viewOffset;
1166     if (context == "page" || context == "window") {
1167       var lOff = getRect(cm.display.lineSpace);
1168       yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
1169       var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
1170       rect.left += xOff; rect.right += xOff;
1171     }
1172     rect.top += yOff; rect.bottom += yOff;
1173     return rect;
1174   }
1175
1176   // Context may be "window", "page", "div", or "local"/null
1177   // Result is in "div" coords
1178   function fromCoordSystem(cm, coords, context) {
1179     if (context == "div") return coords;
1180     var left = coords.left, top = coords.top;
1181     // First move into "page" coordinate system
1182     if (context == "page") {
1183       left -= pageScrollX();
1184       top -= pageScrollY();
1185     } else if (context == "local" || !context) {
1186       var localBox = getRect(cm.display.sizer);
1187       left += localBox.left;
1188       top += localBox.top;
1189     }
1190
1191     var lineSpaceBox = getRect(cm.display.lineSpace);
1192     return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
1193   }
1194
1195   function charCoords(cm, pos, context, lineObj, bias) {
1196     if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1197     return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
1198   }
1199
1200   function cursorCoords(cm, pos, context, lineObj, measurement) {
1201     lineObj = lineObj || getLine(cm.doc, pos.line);
1202     if (!measurement) measurement = measureLine(cm, lineObj);
1203     function get(ch, right) {
1204       var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
1205       if (right) m.left = m.right; else m.right = m.left;
1206       return intoCoordSystem(cm, lineObj, m, context);
1207     }
1208     function getBidi(ch, partPos) {
1209       var part = order[partPos], right = part.level % 2;
1210       if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
1211         part = order[--partPos];
1212         ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
1213         right = true;
1214       } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
1215         part = order[++partPos];
1216         ch = bidiLeft(part) - part.level % 2;
1217         right = false;
1218       }
1219       if (right && ch == part.to && ch > part.from) return get(ch - 1);
1220       return get(ch, right);
1221     }
1222     var order = getOrder(lineObj), ch = pos.ch;
1223     if (!order) return get(ch);
1224     var partPos = getBidiPartAt(order, ch);
1225     var val = getBidi(ch, partPos);
1226     if (bidiOther != null) val.other = getBidi(ch, bidiOther);
1227     return val;
1228   }
1229
1230   function PosWithInfo(line, ch, outside, xRel) {
1231     var pos = new Pos(line, ch);
1232     pos.xRel = xRel;
1233     if (outside) pos.outside = true;
1234     return pos;
1235   }
1236
1237   // Coords must be lineSpace-local
1238   function coordsChar(cm, x, y) {
1239     var doc = cm.doc;
1240     y += cm.display.viewOffset;
1241     if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
1242     var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1243     if (lineNo > last)
1244       return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
1245     if (x < 0) x = 0;
1246
1247     for (;;) {
1248       var lineObj = getLine(doc, lineNo);
1249       var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1250       var merged = collapsedSpanAtEnd(lineObj);
1251       var mergedPos = merged && merged.find();
1252       if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
1253         lineNo = mergedPos.to.line;
1254       else
1255         return found;
1256     }
1257   }
1258
1259   function coordsCharInner(cm, lineObj, lineNo, x, y) {
1260     var innerOff = y - heightAtLine(cm, lineObj);
1261     var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1262     var measurement = measureLine(cm, lineObj);
1263
1264     function getX(ch) {
1265       var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1266                             lineObj, measurement);
1267       wrongLine = true;
1268       if (innerOff > sp.bottom) return sp.left - adjust;
1269       else if (innerOff < sp.top) return sp.left + adjust;
1270       else wrongLine = false;
1271       return sp.left;
1272     }
1273
1274     var bidi = getOrder(lineObj), dist = lineObj.text.length;
1275     var from = lineLeft(lineObj), to = lineRight(lineObj);
1276     var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1277
1278     if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
1279     // Do a binary search between these bounds.
1280     for (;;) {
1281       if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1282         var ch = x < fromX || x - fromX <= toX - x ? from : to;
1283         var xDiff = x - (ch == from ? fromX : toX);
1284         while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1285         var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
1286                               xDiff < 0 ? -1 : xDiff ? 1 : 0);
1287         return pos;
1288       }
1289       var step = Math.ceil(dist / 2), middle = from + step;
1290       if (bidi) {
1291         middle = from;
1292         for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1293       }
1294       var middleX = getX(middle);
1295       if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
1296       else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
1297     }
1298   }
1299
1300   var measureText;
1301   function textHeight(display) {
1302     if (display.cachedTextHeight != null) return display.cachedTextHeight;
1303     if (measureText == null) {
1304       measureText = elt("pre");
1305       // Measure a bunch of lines, for browsers that compute
1306       // fractional heights.
1307       for (var i = 0; i < 49; ++i) {
1308         measureText.appendChild(document.createTextNode("x"));
1309         measureText.appendChild(elt("br"));
1310       }
1311       measureText.appendChild(document.createTextNode("x"));
1312     }
1313     removeChildrenAndAdd(display.measure, measureText);
1314     var height = measureText.offsetHeight / 50;
1315     if (height > 3) display.cachedTextHeight = height;
1316     removeChildren(display.measure);
1317     return height || 1;
1318   }
1319
1320   function charWidth(display) {
1321     if (display.cachedCharWidth != null) return display.cachedCharWidth;
1322     var anchor = elt("span", "x");
1323     var pre = elt("pre", [anchor]);
1324     removeChildrenAndAdd(display.measure, pre);
1325     var width = anchor.offsetWidth;
1326     if (width > 2) display.cachedCharWidth = width;
1327     return width || 10;
1328   }
1329
1330   // OPERATIONS
1331
1332   // Operations are used to wrap changes in such a way that each
1333   // change won't have to update the cursor and display (which would
1334   // be awkward, slow, and error-prone), but instead updates are
1335   // batched and then all combined and executed at once.
1336
1337   var nextOpId = 0;
1338   function startOperation(cm) {
1339     cm.curOp = {
1340       // An array of ranges of lines that have to be updated. See
1341       // updateDisplay.
1342       changes: [],
1343       forceUpdate: false,
1344       updateInput: null,
1345       userSelChange: null,
1346       textChanged: null,
1347       selectionChanged: false,
1348       cursorActivity: false,
1349       updateMaxLine: false,
1350       updateScrollPos: false,
1351       id: ++nextOpId
1352     };
1353     if (!delayedCallbackDepth++) delayedCallbacks = [];
1354   }
1355
1356   function endOperation(cm) {
1357     var op = cm.curOp, doc = cm.doc, display = cm.display;
1358     cm.curOp = null;
1359
1360     if (op.updateMaxLine) computeMaxLength(cm);
1361     if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
1362       var width = measureLineWidth(cm, display.maxLine);
1363       display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1364       display.maxLineChanged = false;
1365       var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1366       if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1367         setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1368     }
1369     var newScrollPos, updated;
1370     if (op.updateScrollPos) {
1371       newScrollPos = op.updateScrollPos;
1372     } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1373       var coords = cursorCoords(cm, doc.sel.head);
1374       newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1375     }
1376     if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) {
1377       updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop, op.forceUpdate);
1378       if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1379     }
1380     if (!updated && op.selectionChanged) updateSelection(cm);
1381     if (op.updateScrollPos) {
1382       display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1383       display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1384       alignHorizontally(cm);
1385       if (op.scrollToPos)
1386         scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
1387     } else if (newScrollPos) {
1388       scrollCursorIntoView(cm);
1389     }
1390     if (op.selectionChanged) restartBlink(cm);
1391
1392     if (cm.state.focused && op.updateInput)
1393       resetInput(cm, op.userSelChange);
1394
1395     var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1396     if (hidden) for (var i = 0; i < hidden.length; ++i)
1397       if (!hidden[i].lines.length) signal(hidden[i], "hide");
1398     if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1399       if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1400
1401     var delayed;
1402     if (!--delayedCallbackDepth) {
1403       delayed = delayedCallbacks;
1404       delayedCallbacks = null;
1405     }
1406     if (op.textChanged)
1407       signal(cm, "change", cm, op.textChanged);
1408     if (op.cursorActivity) signal(cm, "cursorActivity", cm);
1409     if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1410   }
1411
1412   // Wraps a function in an operation. Returns the wrapped function.
1413   function operation(cm1, f) {
1414     return function() {
1415       var cm = cm1 || this, withOp = !cm.curOp;
1416       if (withOp) startOperation(cm);
1417       try { var result = f.apply(cm, arguments); }
1418       finally { if (withOp) endOperation(cm); }
1419       return result;
1420     };
1421   }
1422   function docOperation(f) {
1423     return function() {
1424       var withOp = this.cm && !this.cm.curOp, result;
1425       if (withOp) startOperation(this.cm);
1426       try { result = f.apply(this, arguments); }
1427       finally { if (withOp) endOperation(this.cm); }
1428       return result;
1429     };
1430   }
1431   function runInOp(cm, f) {
1432     var withOp = !cm.curOp, result;
1433     if (withOp) startOperation(cm);
1434     try { result = f(); }
1435     finally { if (withOp) endOperation(cm); }
1436     return result;
1437   }
1438
1439   function regChange(cm, from, to, lendiff) {
1440     if (from == null) from = cm.doc.first;
1441     if (to == null) to = cm.doc.first + cm.doc.size;
1442     cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1443   }
1444
1445   // INPUT HANDLING
1446
1447   function slowPoll(cm) {
1448     if (cm.display.pollingFast) return;
1449     cm.display.poll.set(cm.options.pollInterval, function() {
1450       readInput(cm);
1451       if (cm.state.focused) slowPoll(cm);
1452     });
1453   }
1454
1455   function fastPoll(cm) {
1456     var missed = false;
1457     cm.display.pollingFast = true;
1458     function p() {
1459       var changed = readInput(cm);
1460       if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1461       else {cm.display.pollingFast = false; slowPoll(cm);}
1462     }
1463     cm.display.poll.set(20, p);
1464   }
1465
1466   // prevInput is a hack to work with IME. If we reset the textarea
1467   // on every change, that breaks IME. So we look for changes
1468   // compared to the previous content instead. (Modern browsers have
1469   // events that indicate IME taking place, but these are not widely
1470   // supported or compatible enough yet to rely on.)
1471   function readInput(cm) {
1472     var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1473     if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
1474     if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
1475       input.value = input.value.substring(0, input.value.length - 1);
1476       cm.state.fakedLastChar = false;
1477     }
1478     var text = input.value;
1479     if (text == prevInput && posEq(sel.from, sel.to)) return false;
1480     if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
1481       resetInput(cm, true);
1482       return false;
1483     }
1484
1485     var withOp = !cm.curOp;
1486     if (withOp) startOperation(cm);
1487     sel.shift = false;
1488     var same = 0, l = Math.min(prevInput.length, text.length);
1489     while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1490     var from = sel.from, to = sel.to;
1491     if (same < prevInput.length)
1492       from = Pos(from.line, from.ch - (prevInput.length - same));
1493     else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1494       to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1495
1496     var updateInput = cm.curOp.updateInput;
1497     var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
1498                        origin: cm.state.pasteIncoming ? "paste" : "+input"};
1499     makeChange(cm.doc, changeEvent, "end");
1500     cm.curOp.updateInput = updateInput;
1501     signalLater(cm, "inputRead", cm, changeEvent);
1502
1503     if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1504     else cm.display.prevInput = text;
1505     if (withOp) endOperation(cm);
1506     cm.state.pasteIncoming = false;
1507     return true;
1508   }
1509
1510   function resetInput(cm, user) {
1511     var minimal, selected, doc = cm.doc;
1512     if (!posEq(doc.sel.from, doc.sel.to)) {
1513       cm.display.prevInput = "";
1514       minimal = hasCopyEvent &&
1515         (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1516       var content = minimal ? "-" : selected || cm.getSelection();
1517       cm.display.input.value = content;
1518       if (cm.state.focused) selectInput(cm.display.input);
1519       if (ie && !ie_lt9) cm.display.inputHasSelection = content;
1520     } else if (user) {
1521       cm.display.prevInput = cm.display.input.value = "";
1522       if (ie && !ie_lt9) cm.display.inputHasSelection = null;
1523     }
1524     cm.display.inaccurateSelection = minimal;
1525   }
1526
1527   function focusInput(cm) {
1528     if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1529       cm.display.input.focus();
1530   }
1531
1532   function isReadOnly(cm) {
1533     return cm.options.readOnly || cm.doc.cantEdit;
1534   }
1535
1536   // EVENT HANDLERS
1537
1538   function registerEventHandlers(cm) {
1539     var d = cm.display;
1540     on(d.scroller, "mousedown", operation(cm, onMouseDown));
1541     if (ie)
1542       on(d.scroller, "dblclick", operation(cm, function(e) {
1543         if (signalDOMEvent(cm, e)) return;
1544         var pos = posFromMouse(cm, e);
1545         if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
1546         e_preventDefault(e);
1547         var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
1548         extendSelection(cm.doc, word.from, word.to);
1549       }));
1550     else
1551       on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
1552     on(d.lineSpace, "selectstart", function(e) {
1553       if (!eventInWidget(d, e)) e_preventDefault(e);
1554     });
1555     // Gecko browsers fire contextmenu *after* opening the menu, at
1556     // which point we can't mess with it anymore. Context menu is
1557     // handled in onMouseDown for Gecko.
1558     if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1559
1560     on(d.scroller, "scroll", function() {
1561       if (d.scroller.clientHeight) {
1562         setScrollTop(cm, d.scroller.scrollTop);
1563         setScrollLeft(cm, d.scroller.scrollLeft, true);
1564         signal(cm, "scroll", cm);
1565       }
1566     });
1567     on(d.scrollbarV, "scroll", function() {
1568       if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1569     });
1570     on(d.scrollbarH, "scroll", function() {
1571       if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1572     });
1573
1574     on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1575     on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1576
1577     function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1578     on(d.scrollbarH, "mousedown", reFocus);
1579     on(d.scrollbarV, "mousedown", reFocus);
1580     // Prevent wrapper from ever scrolling
1581     on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1582
1583     var resizeTimer;
1584     function onResize() {
1585       if (resizeTimer == null) resizeTimer = setTimeout(function() {
1586         resizeTimer = null;
1587         // Might be a text scaling operation, clear size caches.
1588         d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
1589         clearCaches(cm);
1590         runInOp(cm, bind(regChange, cm));
1591       }, 100);
1592     }
1593     on(window, "resize", onResize);
1594     // Above handler holds on to the editor and its data structures.
1595     // Here we poll to unregister it when the editor is no longer in
1596     // the document, so that it can be garbage-collected.
1597     function unregister() {
1598       for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1599       if (p) setTimeout(unregister, 5000);
1600       else off(window, "resize", onResize);
1601     }
1602     setTimeout(unregister, 5000);
1603
1604     on(d.input, "keyup", operation(cm, function(e) {
1605       if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1606       if (e.keyCode == 16) cm.doc.sel.shift = false;
1607     }));
1608     on(d.input, "input", bind(fastPoll, cm));
1609     on(d.input, "keydown", operation(cm, onKeyDown));
1610     on(d.input, "keypress", operation(cm, onKeyPress));
1611     on(d.input, "focus", bind(onFocus, cm));
1612     on(d.input, "blur", bind(onBlur, cm));
1613
1614     function drag_(e) {
1615       if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1616       e_stop(e);
1617     }
1618     if (cm.options.dragDrop) {
1619       on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1620       on(d.scroller, "dragenter", drag_);
1621       on(d.scroller, "dragover", drag_);
1622       on(d.scroller, "drop", operation(cm, onDrop));
1623     }
1624     on(d.scroller, "paste", function(e) {
1625       if (eventInWidget(d, e)) return;
1626       focusInput(cm);
1627       fastPoll(cm);
1628     });
1629     on(d.input, "paste", function() {
1630       // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
1631       // Add a char to the end of textarea before paste occur so that
1632       // selection doesn't span to the end of textarea.
1633       if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
1634         var start = d.input.selectionStart, end = d.input.selectionEnd;
1635         d.input.value += "$";
1636         d.input.selectionStart = start;
1637         d.input.selectionEnd = end;
1638         cm.state.fakedLastChar = true;
1639       }
1640       cm.state.pasteIncoming = true;
1641       fastPoll(cm);
1642     });
1643
1644     function prepareCopy() {
1645       if (d.inaccurateSelection) {
1646         d.prevInput = "";
1647         d.inaccurateSelection = false;
1648         d.input.value = cm.getSelection();
1649         selectInput(d.input);
1650       }
1651     }
1652     on(d.input, "cut", prepareCopy);
1653     on(d.input, "copy", prepareCopy);
1654
1655     // Needed to handle Tab key in KHTML
1656     if (khtml) on(d.sizer, "mouseup", function() {
1657         if (document.activeElement == d.input) d.input.blur();
1658         focusInput(cm);
1659     });
1660   }
1661
1662   function eventInWidget(display, e) {
1663     for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1664       if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
1665     }
1666   }
1667
1668   function posFromMouse(cm, e, liberal) {
1669     var display = cm.display;
1670     if (!liberal) {
1671       var target = e_target(e);
1672       if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1673           target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1674           target == display.scrollbarFiller || target == display.gutterFiller) return null;
1675     }
1676     var x, y, space = getRect(display.lineSpace);
1677     // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1678     try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1679     return coordsChar(cm, x - space.left, y - space.top);
1680   }
1681
1682   var lastClick, lastDoubleClick;
1683   function onMouseDown(e) {
1684     if (signalDOMEvent(this, e)) return;
1685     var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1686     sel.shift = e.shiftKey;
1687
1688     if (eventInWidget(display, e)) {
1689       if (!webkit) {
1690         display.scroller.draggable = false;
1691         setTimeout(function(){display.scroller.draggable = true;}, 100);
1692       }
1693       return;
1694     }
1695     if (clickInGutter(cm, e)) return;
1696     var start = posFromMouse(cm, e);
1697
1698     switch (e_button(e)) {
1699     case 3:
1700       if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1701       return;
1702     case 2:
1703       if (webkit) cm.state.lastMiddleDown = +new Date;
1704       if (start) extendSelection(cm.doc, start);
1705       setTimeout(bind(focusInput, cm), 20);
1706       e_preventDefault(e);
1707       return;
1708     }
1709     // For button 1, if it was clicked inside the editor
1710     // (posFromMouse returning non-null), we have to adjust the
1711     // selection.
1712     if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1713
1714     if (!cm.state.focused) onFocus(cm);
1715
1716     var now = +new Date, type = "single";
1717     if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1718       type = "triple";
1719       e_preventDefault(e);
1720       setTimeout(bind(focusInput, cm), 20);
1721       selectLine(cm, start.line);
1722     } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1723       type = "double";
1724       lastDoubleClick = {time: now, pos: start};
1725       e_preventDefault(e);
1726       var word = findWordAt(getLine(doc, start.line).text, start);
1727       extendSelection(cm.doc, word.from, word.to);
1728     } else { lastClick = {time: now, pos: start}; }
1729
1730     var last = start;
1731     if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1732         !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1733       var dragEnd = operation(cm, function(e2) {
1734         if (webkit) display.scroller.draggable = false;
1735         cm.state.draggingText = false;
1736         off(document, "mouseup", dragEnd);
1737         off(display.scroller, "drop", dragEnd);
1738         if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1739           e_preventDefault(e2);
1740           extendSelection(cm.doc, start);
1741           focusInput(cm);
1742         }
1743       });
1744       // Let the drag handler handle this.
1745       if (webkit) display.scroller.draggable = true;
1746       cm.state.draggingText = dragEnd;
1747       // IE's approach to draggable
1748       if (display.scroller.dragDrop) display.scroller.dragDrop();
1749       on(document, "mouseup", dragEnd);
1750       on(display.scroller, "drop", dragEnd);
1751       return;
1752     }
1753     e_preventDefault(e);
1754     if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1755
1756     var startstart = sel.from, startend = sel.to, lastPos = start;
1757
1758     function doSelect(cur) {
1759       if (posEq(lastPos, cur)) return;
1760       lastPos = cur;
1761
1762       if (type == "single") {
1763         extendSelection(cm.doc, clipPos(doc, start), cur);
1764         return;
1765       }
1766
1767       startstart = clipPos(doc, startstart);
1768       startend = clipPos(doc, startend);
1769       if (type == "double") {
1770         var word = findWordAt(getLine(doc, cur.line).text, cur);
1771         if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1772         else extendSelection(cm.doc, startstart, word.to);
1773       } else if (type == "triple") {
1774         if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1775         else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1776       }
1777     }
1778
1779     var editorSize = getRect(display.wrapper);
1780     // Used to ensure timeout re-tries don't fire when another extend
1781     // happened in the meantime (clearTimeout isn't reliable -- at
1782     // least on Chrome, the timeouts still happen even when cleared,
1783     // if the clear happens after their scheduled firing time).
1784     var counter = 0;
1785
1786     function extend(e) {
1787       var curCount = ++counter;
1788       var cur = posFromMouse(cm, e, true);
1789       if (!cur) return;
1790       if (!posEq(cur, last)) {
1791         if (!cm.state.focused) onFocus(cm);
1792         last = cur;
1793         doSelect(cur);
1794         var visible = visibleLines(display, doc);
1795         if (cur.line >= visible.to || cur.line < visible.from)
1796           setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1797       } else {
1798         var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1799         if (outside) setTimeout(operation(cm, function() {
1800           if (counter != curCount) return;
1801           display.scroller.scrollTop += outside;
1802           extend(e);
1803         }), 50);
1804       }
1805     }
1806
1807     function done(e) {
1808       counter = Infinity;
1809       e_preventDefault(e);
1810       focusInput(cm);
1811       off(document, "mousemove", move);
1812       off(document, "mouseup", up);
1813     }
1814
1815     var move = operation(cm, function(e) {
1816       if (!ie && !e_button(e)) done(e);
1817       else extend(e);
1818     });
1819     var up = operation(cm, done);
1820     on(document, "mousemove", move);
1821     on(document, "mouseup", up);
1822   }
1823
1824   function gutterEvent(cm, e, type, prevent, signalfn) {
1825     try { var mX = e.clientX, mY = e.clientY; }
1826     catch(e) { return false; }
1827     if (mX >= Math.floor(getRect(cm.display.gutters).right)) return false;
1828     if (prevent) e_preventDefault(e);
1829
1830     var display = cm.display;
1831     var lineBox = getRect(display.lineDiv);
1832
1833     if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
1834     mY -= lineBox.top - display.viewOffset;
1835
1836     for (var i = 0; i < cm.options.gutters.length; ++i) {
1837       var g = display.gutters.childNodes[i];
1838       if (g && getRect(g).right >= mX) {
1839         var line = lineAtHeight(cm.doc, mY);
1840         var gutter = cm.options.gutters[i];
1841         signalfn(cm, type, cm, line, gutter, e);
1842         return e_defaultPrevented(e);
1843       }
1844     }
1845   }
1846
1847   function contextMenuInGutter(cm, e) {
1848     if (!hasHandler(cm, "gutterContextMenu")) return false;
1849     return gutterEvent(cm, e, "gutterContextMenu", false, signal);
1850   }
1851
1852   function clickInGutter(cm, e) {
1853     return gutterEvent(cm, e, "gutterClick", true, signalLater);
1854   }
1855
1856   // Kludge to work around strange IE behavior where it'll sometimes
1857   // re-fire a series of drag-related events right after the drop (#1551)
1858   var lastDrop = 0;
1859
1860   function onDrop(e) {
1861     var cm = this;
1862     if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1863       return;
1864     e_preventDefault(e);
1865     if (ie) lastDrop = +new Date;
1866     var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1867     if (!pos || isReadOnly(cm)) return;
1868     if (files && files.length && window.FileReader && window.File) {
1869       var n = files.length, text = Array(n), read = 0;
1870       var loadFile = function(file, i) {
1871         var reader = new FileReader;
1872         reader.onload = function() {
1873           text[i] = reader.result;
1874           if (++read == n) {
1875             pos = clipPos(cm.doc, pos);
1876             makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1877           }
1878         };
1879         reader.readAsText(file);
1880       };
1881       for (var i = 0; i < n; ++i) loadFile(files[i], i);
1882     } else {
1883       // Don't do a replace if the drop happened inside of the selected text.
1884       if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1885         cm.state.draggingText(e);
1886         // Ensure the editor is re-focused
1887         setTimeout(bind(focusInput, cm), 20);
1888         return;
1889       }
1890       try {
1891         var text = e.dataTransfer.getData("Text");
1892         if (text) {
1893           var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1894           setSelection(cm.doc, pos, pos);
1895           if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1896           cm.replaceSelection(text, null, "paste");
1897           focusInput(cm);
1898           onFocus(cm);
1899         }
1900       }
1901       catch(e){}
1902     }
1903   }
1904
1905   function onDragStart(cm, e) {
1906     if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
1907     if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
1908
1909     var txt = cm.getSelection();
1910     e.dataTransfer.setData("Text", txt);
1911
1912     // Use dummy image instead of default browsers image.
1913     // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1914     if (e.dataTransfer.setDragImage && !safari) {
1915       var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1916       img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1917       if (opera) {
1918         img.width = img.height = 1;
1919         cm.display.wrapper.appendChild(img);
1920         // Force a relayout, or Opera won't use our image for some obscure reason
1921         img._top = img.offsetTop;
1922       }
1923       e.dataTransfer.setDragImage(img, 0, 0);
1924       if (opera) img.parentNode.removeChild(img);
1925     }
1926   }
1927
1928   function setScrollTop(cm, val) {
1929     if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1930     cm.doc.scrollTop = val;
1931     if (!gecko) updateDisplay(cm, [], val);
1932     if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1933     if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1934     if (gecko) updateDisplay(cm, []);
1935     startWorker(cm, 100);
1936   }
1937   function setScrollLeft(cm, val, isScroller) {
1938     if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1939     val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1940     cm.doc.scrollLeft = val;
1941     alignHorizontally(cm);
1942     if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1943     if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1944   }
1945
1946   // Since the delta values reported on mouse wheel events are
1947   // unstandardized between browsers and even browser versions, and
1948   // generally horribly unpredictable, this code starts by measuring
1949   // the scroll effect that the first few mouse wheel events have,
1950   // and, from that, detects the way it can convert deltas to pixel
1951   // offsets afterwards.
1952   //
1953   // The reason we want to know the amount a wheel event will scroll
1954   // is that it gives us a chance to update the display before the
1955   // actual scrolling happens, reducing flickering.
1956
1957   var wheelSamples = 0, wheelPixelsPerUnit = null;
1958   // Fill in a browser-detected starting value on browsers where we
1959   // know one. These don't have to be accurate -- the result of them
1960   // being wrong would just be a slight flicker on the first wheel
1961   // scroll (if it is large enough).
1962   if (ie) wheelPixelsPerUnit = -.53;
1963   else if (gecko) wheelPixelsPerUnit = 15;
1964   else if (chrome) wheelPixelsPerUnit = -.7;
1965   else if (safari) wheelPixelsPerUnit = -1/3;
1966
1967   function onScrollWheel(cm, e) {
1968     var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1969     if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1970     if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1971     else if (dy == null) dy = e.wheelDelta;
1972
1973     var display = cm.display, scroll = display.scroller;
1974     // Quit if there's nothing to scroll here
1975     if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
1976           dy && scroll.scrollHeight > scroll.clientHeight)) return;
1977
1978     // Webkit browsers on OS X abort momentum scrolls when the target
1979     // of the scroll event is removed from the scrollable element.
1980     // This hack (see related code in patchDisplay) makes sure the
1981     // element is kept around.
1982     if (dy && mac && webkit) {
1983       for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1984         if (cur.lineObj) {
1985           cm.display.currentWheelTarget = cur;
1986           break;
1987         }
1988       }
1989     }
1990
1991     // On some browsers, horizontal scrolling will cause redraws to
1992     // happen before the gutter has been realigned, causing it to
1993     // wriggle around in a most unseemly way. When we have an
1994     // estimated pixels/delta value, we just handle horizontal
1995     // scrolling entirely here. It'll be slightly off from native, but
1996     // better than glitching out.
1997     if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1998       if (dy)
1999         setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
2000       setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
2001       e_preventDefault(e);
2002       display.wheelStartX = null; // Abort measurement, if in progress
2003       return;
2004     }
2005
2006     if (dy && wheelPixelsPerUnit != null) {
2007       var pixels = dy * wheelPixelsPerUnit;
2008       var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
2009       if (pixels < 0) top = Math.max(0, top + pixels - 50);
2010       else bot = Math.min(cm.doc.height, bot + pixels + 50);
2011       updateDisplay(cm, [], {top: top, bottom: bot});
2012     }
2013
2014     if (wheelSamples < 20) {
2015       if (display.wheelStartX == null) {
2016         display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
2017         display.wheelDX = dx; display.wheelDY = dy;
2018         setTimeout(function() {
2019           if (display.wheelStartX == null) return;
2020           var movedX = scroll.scrollLeft - display.wheelStartX;
2021           var movedY = scroll.scrollTop - display.wheelStartY;
2022           var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
2023             (movedX && display.wheelDX && movedX / display.wheelDX);
2024           display.wheelStartX = display.wheelStartY = null;
2025           if (!sample) return;
2026           wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
2027           ++wheelSamples;
2028         }, 200);
2029       } else {
2030         display.wheelDX += dx; display.wheelDY += dy;
2031       }
2032     }
2033   }
2034
2035   function doHandleBinding(cm, bound, dropShift) {
2036     if (typeof bound == "string") {
2037       bound = commands[bound];
2038       if (!bound) return false;
2039     }
2040     // Ensure previous input has been read, so that the handler sees a
2041     // consistent view of the document
2042     if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
2043     var doc = cm.doc, prevShift = doc.sel.shift, done = false;
2044     try {
2045       if (isReadOnly(cm)) cm.state.suppressEdits = true;
2046       if (dropShift) doc.sel.shift = false;
2047       done = bound(cm) != Pass;
2048     } finally {
2049       doc.sel.shift = prevShift;
2050       cm.state.suppressEdits = false;
2051     }
2052     return done;
2053   }
2054
2055   function allKeyMaps(cm) {
2056     var maps = cm.state.keyMaps.slice(0);
2057     if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
2058     maps.push(cm.options.keyMap);
2059     return maps;
2060   }
2061
2062   var maybeTransition;
2063   function handleKeyBinding(cm, e) {
2064     // Handle auto keymap transitions
2065     var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
2066     clearTimeout(maybeTransition);
2067     if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
2068       if (getKeyMap(cm.options.keyMap) == startMap) {
2069         cm.options.keyMap = (next.call ? next.call(null, cm) : next);
2070         keyMapChanged(cm);
2071       }
2072     }, 50);
2073
2074     var name = keyName(e, true), handled = false;
2075     if (!name) return false;
2076     var keymaps = allKeyMaps(cm);
2077
2078     if (e.shiftKey) {
2079       // First try to resolve full name (including 'Shift-'). Failing
2080       // that, see if there is a cursor-motion command (starting with
2081       // 'go') bound to the keyname without 'Shift-'.
2082       handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
2083              || lookupKey(name, keymaps, function(b) {
2084                   if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
2085                     return doHandleBinding(cm, b);
2086                 });
2087     } else {
2088       handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
2089     }
2090
2091     if (handled) {
2092       e_preventDefault(e);
2093       restartBlink(cm);
2094       if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
2095       signalLater(cm, "keyHandled", cm, name, e);
2096     }
2097     return handled;
2098   }
2099
2100   function handleCharBinding(cm, e, ch) {
2101     var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
2102                             function(b) { return doHandleBinding(cm, b, true); });
2103     if (handled) {
2104       e_preventDefault(e);
2105       restartBlink(cm);
2106       signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
2107     }
2108     return handled;
2109   }
2110
2111   var lastStoppedKey = null;
2112   function onKeyDown(e) {
2113     var cm = this;
2114     if (!cm.state.focused) onFocus(cm);
2115     if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2116     if (ie && e.keyCode == 27) e.returnValue = false;
2117     var code = e.keyCode;
2118     // IE does strange things with escape.
2119     cm.doc.sel.shift = code == 16 || e.shiftKey;
2120     // First give onKeyEvent option a chance to handle this.
2121     var handled = handleKeyBinding(cm, e);
2122     if (opera) {
2123       lastStoppedKey = handled ? code : null;
2124       // Opera has no cut event... we try to at least catch the key combo
2125       if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
2126         cm.replaceSelection("");
2127     }
2128   }
2129
2130   function onKeyPress(e) {
2131     var cm = this;
2132     if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2133     var keyCode = e.keyCode, charCode = e.charCode;
2134     if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2135     if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2136     var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2137     if (this.options.electricChars && this.doc.mode.electricChars &&
2138         this.options.smartIndent && !isReadOnly(this) &&
2139         this.doc.mode.electricChars.indexOf(ch) > -1)
2140       setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2141     if (handleCharBinding(cm, e, ch)) return;
2142     if (ie && !ie_lt9) cm.display.inputHasSelection = null;
2143     fastPoll(cm);
2144   }
2145
2146   function onFocus(cm) {
2147     if (cm.options.readOnly == "nocursor") return;
2148     if (!cm.state.focused) {
2149       signal(cm, "focus", cm);
2150       cm.state.focused = true;
2151       if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2152         cm.display.wrapper.className += " CodeMirror-focused";
2153       if (!cm.curOp) {
2154         resetInput(cm, true);
2155         if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
2156       }
2157     }
2158     slowPoll(cm);
2159     restartBlink(cm);
2160   }
2161   function onBlur(cm) {
2162     if (cm.state.focused) {
2163       signal(cm, "blur", cm);
2164       cm.state.focused = false;
2165       cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2166     }
2167     clearInterval(cm.display.blinker);
2168     setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2169   }
2170
2171   var detectingSelectAll;
2172   function onContextMenu(cm, e) {
2173     if (signalDOMEvent(cm, e, "contextmenu")) return;
2174     var display = cm.display, sel = cm.doc.sel;
2175     if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
2176
2177     var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2178     if (!pos || opera) return; // Opera is difficult.
2179     if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2180       operation(cm, setSelection)(cm.doc, pos, pos);
2181
2182     var oldCSS = display.input.style.cssText;
2183     display.inputDiv.style.position = "absolute";
2184     display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2185       "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2186       "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2187     focusInput(cm);
2188     resetInput(cm, true);
2189     // Adds "Select all" to context menu in FF
2190     if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2191
2192     function prepareSelectAllHack() {
2193       if (display.input.selectionStart != null) {
2194         var extval = display.input.value = "\u200b" + (posEq(sel.from, sel.to) ? "" : display.input.value);
2195         display.prevInput = "\u200b";
2196         display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2197       }
2198     }
2199     function rehide() {
2200       display.inputDiv.style.position = "relative";
2201       display.input.style.cssText = oldCSS;
2202       if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2203       slowPoll(cm);
2204
2205       // Try to detect the user choosing select-all
2206       if (display.input.selectionStart != null) {
2207         if (!ie || ie_lt9) prepareSelectAllHack();
2208         clearTimeout(detectingSelectAll);
2209         var i = 0, poll = function(){
2210           if (display.prevInput == " " && display.input.selectionStart == 0)
2211             operation(cm, commands.selectAll)(cm);
2212           else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2213           else resetInput(cm);
2214         };
2215         detectingSelectAll = setTimeout(poll, 200);
2216       }
2217     }
2218
2219     if (ie && !ie_lt9) prepareSelectAllHack();
2220     if (captureMiddleClick) {
2221       e_stop(e);
2222       var mouseup = function() {
2223         off(window, "mouseup", mouseup);
2224         setTimeout(rehide, 20);
2225       };
2226       on(window, "mouseup", mouseup);
2227     } else {
2228       setTimeout(rehide, 50);
2229     }
2230   }
2231
2232   // UPDATING
2233
2234   var changeEnd = CodeMirror.changeEnd = function(change) {
2235     if (!change.text) return change.to;
2236     return Pos(change.from.line + change.text.length - 1,
2237                lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2238   };
2239
2240   // Make sure a position will be valid after the given change.
2241   function clipPostChange(doc, change, pos) {
2242     if (!posLess(change.from, pos)) return clipPos(doc, pos);
2243     var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2244     if (pos.line > change.to.line + diff) {
2245       var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2246       if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2247       return clipToLen(pos, getLine(doc, preLine).text.length);
2248     }
2249     if (pos.line == change.to.line + diff)
2250       return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2251                        getLine(doc, change.to.line).text.length - change.to.ch);
2252     var inside = pos.line - change.from.line;
2253     return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2254   }
2255
2256   // Hint can be null|"end"|"start"|"around"|{anchor,head}
2257   function computeSelAfterChange(doc, change, hint) {
2258     if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2259       return {anchor: clipPostChange(doc, change, hint.anchor),
2260               head: clipPostChange(doc, change, hint.head)};
2261
2262     if (hint == "start") return {anchor: change.from, head: change.from};
2263
2264     var end = changeEnd(change);
2265     if (hint == "around") return {anchor: change.from, head: end};
2266     if (hint == "end") return {anchor: end, head: end};
2267
2268     // hint is null, leave the selection alone as much as possible
2269     var adjustPos = function(pos) {
2270       if (posLess(pos, change.from)) return pos;
2271       if (!posLess(change.to, pos)) return end;
2272
2273       var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2274       if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2275       return Pos(line, ch);
2276     };
2277     return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2278   }
2279
2280   function filterChange(doc, change, update) {
2281     var obj = {
2282       canceled: false,
2283       from: change.from,
2284       to: change.to,
2285       text: change.text,
2286       origin: change.origin,
2287       cancel: function() { this.canceled = true; }
2288     };
2289     if (update) obj.update = function(from, to, text, origin) {
2290       if (from) this.from = clipPos(doc, from);
2291       if (to) this.to = clipPos(doc, to);
2292       if (text) this.text = text;
2293       if (origin !== undefined) this.origin = origin;
2294     };
2295     signal(doc, "beforeChange", doc, obj);
2296     if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2297
2298     if (obj.canceled) return null;
2299     return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2300   }
2301
2302   // Replace the range from from to to by the strings in replacement.
2303   // change is a {from, to, text [, origin]} object
2304   function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2305     if (doc.cm) {
2306       if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2307       if (doc.cm.state.suppressEdits) return;
2308     }
2309
2310     if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2311       change = filterChange(doc, change, true);
2312       if (!change) return;
2313     }
2314
2315     // Possibly split or suppress the update based on the presence
2316     // of read-only spans in its range.
2317     var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2318     if (split) {
2319       for (var i = split.length - 1; i >= 1; --i)
2320         makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2321       if (split.length)
2322         makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2323     } else {
2324       makeChangeNoReadonly(doc, change, selUpdate);
2325     }
2326   }
2327
2328   function makeChangeNoReadonly(doc, change, selUpdate) {
2329     var selAfter = computeSelAfterChange(doc, change, selUpdate);
2330     addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2331
2332     makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2333     var rebased = [];
2334
2335     linkedDocs(doc, function(doc, sharedHist) {
2336       if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2337         rebaseHist(doc.history, change);
2338         rebased.push(doc.history);
2339       }
2340       makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2341     });
2342   }
2343
2344   function makeChangeFromHistory(doc, type) {
2345     if (doc.cm && doc.cm.state.suppressEdits) return;
2346
2347     var hist = doc.history;
2348     var event = (type == "undo" ? hist.done : hist.undone).pop();
2349     if (!event) return;
2350
2351     var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2352                 anchorAfter: event.anchorBefore, headAfter: event.headBefore,
2353                 generation: hist.generation};
2354     (type == "undo" ? hist.undone : hist.done).push(anti);
2355     hist.generation = event.generation || ++hist.maxGeneration;
2356
2357     var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
2358
2359     for (var i = event.changes.length - 1; i >= 0; --i) {
2360       var change = event.changes[i];
2361       change.origin = type;
2362       if (filter && !filterChange(doc, change, false)) {
2363         (type == "undo" ? hist.done : hist.undone).length = 0;
2364         return;
2365       }
2366
2367       anti.changes.push(historyChangeFromChange(doc, change));
2368
2369       var after = i ? computeSelAfterChange(doc, change, null)
2370                     : {anchor: event.anchorBefore, head: event.headBefore};
2371       makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2372       var rebased = [];
2373
2374       linkedDocs(doc, function(doc, sharedHist) {
2375         if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2376           rebaseHist(doc.history, change);
2377           rebased.push(doc.history);
2378         }
2379         makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2380       });
2381     }
2382   }
2383
2384   function shiftDoc(doc, distance) {
2385     function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2386     doc.first += distance;
2387     if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2388     doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2389     doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2390   }
2391
2392   function makeChangeSingleDoc(doc, change, selAfter, spans) {
2393     if (doc.cm && !doc.cm.curOp)
2394       return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2395
2396     if (change.to.line < doc.first) {
2397       shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2398       return;
2399     }
2400     if (change.from.line > doc.lastLine()) return;
2401
2402     // Clip the change to the size of this doc
2403     if (change.from.line < doc.first) {
2404       var shift = change.text.length - 1 - (doc.first - change.from.line);
2405       shiftDoc(doc, shift);
2406       change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2407                 text: [lst(change.text)], origin: change.origin};
2408     }
2409     var last = doc.lastLine();
2410     if (change.to.line > last) {
2411       change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2412                 text: [change.text[0]], origin: change.origin};
2413     }
2414
2415     change.removed = getBetween(doc, change.from, change.to);
2416
2417     if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2418     if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2419     else updateDoc(doc, change, spans, selAfter);
2420   }
2421
2422   function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2423     var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2424
2425     var recomputeMaxLength = false, checkWidthStart = from.line;
2426     if (!cm.options.lineWrapping) {
2427       checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2428       doc.iter(checkWidthStart, to.line + 1, function(line) {
2429         if (line == display.maxLine) {
2430           recomputeMaxLength = true;
2431           return true;
2432         }
2433       });
2434     }
2435
2436     if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
2437       cm.curOp.cursorActivity = true;
2438
2439     updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2440
2441     if (!cm.options.lineWrapping) {
2442       doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2443         var len = lineLength(doc, line);
2444         if (len > display.maxLineLength) {
2445           display.maxLine = line;
2446           display.maxLineLength = len;
2447           display.maxLineChanged = true;
2448           recomputeMaxLength = false;
2449         }
2450       });
2451       if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2452     }
2453
2454     // Adjust frontier, schedule worker
2455     doc.frontier = Math.min(doc.frontier, from.line);
2456     startWorker(cm, 400);
2457
2458     var lendiff = change.text.length - (to.line - from.line) - 1;
2459     // Remember that these lines changed, for updating the display
2460     regChange(cm, from.line, to.line + 1, lendiff);
2461
2462     if (hasHandler(cm, "change")) {
2463       var changeObj = {from: from, to: to,
2464                        text: change.text,
2465                        removed: change.removed,
2466                        origin: change.origin};
2467       if (cm.curOp.textChanged) {
2468         for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2469         cur.next = changeObj;
2470       } else cm.curOp.textChanged = changeObj;
2471     }
2472   }
2473
2474   function replaceRange(doc, code, from, to, origin) {
2475     if (!to) to = from;
2476     if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2477     if (typeof code == "string") code = splitLines(code);
2478     makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2479   }
2480
2481   // POSITION OBJECT
2482
2483   function Pos(line, ch) {
2484     if (!(this instanceof Pos)) return new Pos(line, ch);
2485     this.line = line; this.ch = ch;
2486   }
2487   CodeMirror.Pos = Pos;
2488
2489   function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2490   function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2491   function copyPos(x) {return Pos(x.line, x.ch);}
2492
2493   // SELECTION
2494
2495   function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2496   function clipPos(doc, pos) {
2497     if (pos.line < doc.first) return Pos(doc.first, 0);
2498     var last = doc.first + doc.size - 1;
2499     if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2500     return clipToLen(pos, getLine(doc, pos.line).text.length);
2501   }
2502   function clipToLen(pos, linelen) {
2503     var ch = pos.ch;
2504     if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2505     else if (ch < 0) return Pos(pos.line, 0);
2506     else return pos;
2507   }
2508   function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2509
2510   // If shift is held, this will move the selection anchor. Otherwise,
2511   // it'll set the whole selection.
2512   function extendSelection(doc, pos, other, bias) {
2513     if (doc.sel.shift || doc.sel.extend) {
2514       var anchor = doc.sel.anchor;
2515       if (other) {
2516         var posBefore = posLess(pos, anchor);
2517         if (posBefore != posLess(other, anchor)) {
2518           anchor = pos;
2519           pos = other;
2520         } else if (posBefore != posLess(pos, other)) {
2521           pos = other;
2522         }
2523       }
2524       setSelection(doc, anchor, pos, bias);
2525     } else {
2526       setSelection(doc, pos, other || pos, bias);
2527     }
2528     if (doc.cm) doc.cm.curOp.userSelChange = true;
2529   }
2530
2531   function filterSelectionChange(doc, anchor, head) {
2532     var obj = {anchor: anchor, head: head};
2533     signal(doc, "beforeSelectionChange", doc, obj);
2534     if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2535     obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2536     return obj;
2537   }
2538
2539   // Update the selection. Last two args are only used by
2540   // updateDoc, since they have to be expressed in the line
2541   // numbers before the update.
2542   function setSelection(doc, anchor, head, bias, checkAtomic) {
2543     if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2544       var filtered = filterSelectionChange(doc, anchor, head);
2545       head = filtered.head;
2546       anchor = filtered.anchor;
2547     }
2548
2549     var sel = doc.sel;
2550     sel.goalColumn = null;
2551     if (bias == null) bias = posLess(head, sel.head) ? -1 : 1;
2552     // Skip over atomic spans.
2553     if (checkAtomic || !posEq(anchor, sel.anchor))
2554       anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2555     if (checkAtomic || !posEq(head, sel.head))
2556       head = skipAtomic(doc, head, bias, checkAtomic != "push");
2557
2558     if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2559
2560     sel.anchor = anchor; sel.head = head;
2561     var inv = posLess(head, anchor);
2562     sel.from = inv ? head : anchor;
2563     sel.to = inv ? anchor : head;
2564
2565     if (doc.cm)
2566       doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
2567         doc.cm.curOp.cursorActivity = true;
2568
2569     signalLater(doc, "cursorActivity", doc);
2570   }
2571
2572   function reCheckSelection(cm) {
2573     setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2574   }
2575
2576   function skipAtomic(doc, pos, bias, mayClear) {
2577     var flipped = false, curPos = pos;
2578     var dir = bias || 1;
2579     doc.cantEdit = false;
2580     search: for (;;) {
2581       var line = getLine(doc, curPos.line);
2582       if (line.markedSpans) {
2583         for (var i = 0; i < line.markedSpans.length; ++i) {
2584           var sp = line.markedSpans[i], m = sp.marker;
2585           if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2586               (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2587             if (mayClear) {
2588               signal(m, "beforeCursorEnter");
2589               if (m.explicitlyCleared) {
2590                 if (!line.markedSpans) break;
2591                 else {--i; continue;}
2592               }
2593             }
2594             if (!m.atomic) continue;
2595             var newPos = m.find()[dir < 0 ? "from" : "to"];
2596             if (posEq(newPos, curPos)) {
2597               newPos.ch += dir;
2598               if (newPos.ch < 0) {
2599                 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2600                 else newPos = null;
2601               } else if (newPos.ch > line.text.length) {
2602                 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2603                 else newPos = null;
2604               }
2605               if (!newPos) {
2606                 if (flipped) {
2607                   // Driven in a corner -- no valid cursor position found at all
2608                   // -- try again *with* clearing, if we didn't already
2609                   if (!mayClear) return skipAtomic(doc, pos, bias, true);
2610                   // Otherwise, turn off editing until further notice, and return the start of the doc
2611                   doc.cantEdit = true;
2612                   return Pos(doc.first, 0);
2613                 }
2614                 flipped = true; newPos = pos; dir = -dir;
2615               }
2616             }
2617             curPos = newPos;
2618             continue search;
2619           }
2620         }
2621       }
2622       return curPos;
2623     }
2624   }
2625
2626   // SCROLLING
2627
2628   function scrollCursorIntoView(cm) {
2629     var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
2630     if (!cm.state.focused) return;
2631     var display = cm.display, box = getRect(display.sizer), doScroll = null;
2632     if (coords.top + box.top < 0) doScroll = true;
2633     else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2634     if (doScroll != null && !phantom) {
2635       var hidden = display.cursor.style.display == "none";
2636       if (hidden) {
2637         display.cursor.style.display = "";
2638         display.cursor.style.left = coords.left + "px";
2639         display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2640       }
2641       display.cursor.scrollIntoView(doScroll);
2642       if (hidden) display.cursor.style.display = "none";
2643     }
2644   }
2645
2646   function scrollPosIntoView(cm, pos, margin) {
2647     if (margin == null) margin = 0;
2648     for (;;) {
2649       var changed = false, coords = cursorCoords(cm, pos);
2650       var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2651       var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2652       if (scrollPos.scrollTop != null) {
2653         setScrollTop(cm, scrollPos.scrollTop);
2654         if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2655       }
2656       if (scrollPos.scrollLeft != null) {
2657         setScrollLeft(cm, scrollPos.scrollLeft);
2658         if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2659       }
2660       if (!changed) return coords;
2661     }
2662   }
2663
2664   function scrollIntoView(cm, x1, y1, x2, y2) {
2665     var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2666     if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2667     if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2668   }
2669
2670   function calculateScrollPos(cm, x1, y1, x2, y2) {
2671     var display = cm.display, snapMargin = textHeight(cm.display);
2672     if (y1 < 0) y1 = 0;
2673     var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2674     var docBottom = cm.doc.height + paddingVert(display);
2675     var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
2676     if (y1 < screentop) {
2677       result.scrollTop = atTop ? 0 : y1;
2678     } else if (y2 > screentop + screen) {
2679       var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
2680       if (newTop != screentop) result.scrollTop = newTop;
2681     }
2682
2683     var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2684     x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2685     var gutterw = display.gutters.offsetWidth;
2686     var atLeft = x1 < gutterw + 10;
2687     if (x1 < screenleft + gutterw || atLeft) {
2688       if (atLeft) x1 = 0;
2689       result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2690     } else if (x2 > screenw + screenleft - 3) {
2691       result.scrollLeft = x2 + 10 - screenw;
2692     }
2693     return result;
2694   }
2695
2696   function updateScrollPos(cm, left, top) {
2697     cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
2698                                 scrollTop: top == null ? cm.doc.scrollTop : top};
2699   }
2700
2701   function addToScrollPos(cm, left, top) {
2702     var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2703     var scroll = cm.display.scroller;
2704     pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2705     pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2706   }
2707
2708   // API UTILITIES
2709
2710   function indentLine(cm, n, how, aggressive) {
2711     var doc = cm.doc;
2712     if (how == null) how = "add";
2713     if (how == "smart") {
2714       if (!cm.doc.mode.indent) how = "prev";
2715       else var state = getStateBefore(cm, n);
2716     }
2717
2718     var tabSize = cm.options.tabSize;
2719     var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2720     var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2721     if (how == "smart") {
2722       indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2723       if (indentation == Pass) {
2724         if (!aggressive) return;
2725         how = "prev";
2726       }
2727     }
2728     if (how == "prev") {
2729       if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2730       else indentation = 0;
2731     } else if (how == "add") {
2732       indentation = curSpace + cm.options.indentUnit;
2733     } else if (how == "subtract") {
2734       indentation = curSpace - cm.options.indentUnit;
2735     } else if (typeof how == "number") {
2736       indentation = curSpace + how;
2737     }
2738     indentation = Math.max(0, indentation);
2739
2740     var indentString = "", pos = 0;
2741     if (cm.options.indentWithTabs)
2742       for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2743     if (pos < indentation) indentString += spaceStr(indentation - pos);
2744
2745     if (indentString != curSpaceString)
2746       replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2747     line.stateAfter = null;
2748   }
2749
2750   function changeLine(cm, handle, op) {
2751     var no = handle, line = handle, doc = cm.doc;
2752     if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2753     else no = lineNo(handle);
2754     if (no == null) return null;
2755     if (op(line, no)) regChange(cm, no, no + 1);
2756     else return null;
2757     return line;
2758   }
2759
2760   function findPosH(doc, pos, dir, unit, visually) {
2761     var line = pos.line, ch = pos.ch, origDir = dir;
2762     var lineObj = getLine(doc, line);
2763     var possible = true;
2764     function findNextLine() {
2765       var l = line + dir;
2766       if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2767       line = l;
2768       return lineObj = getLine(doc, l);
2769     }
2770     function moveOnce(boundToLine) {
2771       var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2772       if (next == null) {
2773         if (!boundToLine && findNextLine()) {
2774           if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2775           else ch = dir < 0 ? lineObj.text.length : 0;
2776         } else return (possible = false);
2777       } else ch = next;
2778       return true;
2779     }
2780
2781     if (unit == "char") moveOnce();
2782     else if (unit == "column") moveOnce(true);
2783     else if (unit == "word" || unit == "group") {
2784       var sawType = null, group = unit == "group";
2785       for (var first = true;; first = false) {
2786         if (dir < 0 && !moveOnce(!first)) break;
2787         var cur = lineObj.text.charAt(ch) || "\n";
2788         var type = isWordChar(cur) ? "w"
2789           : !group ? null
2790           : /\s/.test(cur) ? null
2791           : "p";
2792         if (sawType && sawType != type) {
2793           if (dir < 0) {dir = 1; moveOnce();}
2794           break;
2795         }
2796         if (type) sawType = type;
2797         if (dir > 0 && !moveOnce(!first)) break;
2798       }
2799     }
2800     var result = skipAtomic(doc, Pos(line, ch), origDir, true);
2801     if (!possible) result.hitSide = true;
2802     return result;
2803   }
2804
2805   function findPosV(cm, pos, dir, unit) {
2806     var doc = cm.doc, x = pos.left, y;
2807     if (unit == "page") {
2808       var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2809       y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2810     } else if (unit == "line") {
2811       y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2812     }
2813     for (;;) {
2814       var target = coordsChar(cm, x, y);
2815       if (!target.outside) break;
2816       if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2817       y += dir * 5;
2818     }
2819     return target;
2820   }
2821
2822   function findWordAt(line, pos) {
2823     var start = pos.ch, end = pos.ch;
2824     if (line) {
2825       if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
2826       var startChar = line.charAt(start);
2827       var check = isWordChar(startChar) ? isWordChar
2828         : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2829         : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2830       while (start > 0 && check(line.charAt(start - 1))) --start;
2831       while (end < line.length && check(line.charAt(end))) ++end;
2832     }
2833     return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2834   }
2835
2836   function selectLine(cm, line) {
2837     extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2838   }
2839
2840   // PROTOTYPE
2841
2842   // The publicly visible API. Note that operation(null, f) means
2843   // 'wrap f in an operation, performed on its `this` parameter'
2844
2845   CodeMirror.prototype = {
2846     constructor: CodeMirror,
2847     focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2848
2849     setOption: function(option, value) {
2850       var options = this.options, old = options[option];
2851       if (options[option] == value && option != "mode") return;
2852       options[option] = value;
2853       if (optionHandlers.hasOwnProperty(option))
2854         operation(this, optionHandlers[option])(this, value, old);
2855     },
2856
2857     getOption: function(option) {return this.options[option];},
2858     getDoc: function() {return this.doc;},
2859
2860     addKeyMap: function(map, bottom) {
2861       this.state.keyMaps[bottom ? "push" : "unshift"](map);
2862     },
2863     removeKeyMap: function(map) {
2864       var maps = this.state.keyMaps;
2865       for (var i = 0; i < maps.length; ++i)
2866         if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
2867           maps.splice(i, 1);
2868           return true;
2869         }
2870     },
2871
2872     addOverlay: operation(null, function(spec, options) {
2873       var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2874       if (mode.startState) throw new Error("Overlays may not be stateful.");
2875       this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2876       this.state.modeGen++;
2877       regChange(this);
2878     }),
2879     removeOverlay: operation(null, function(spec) {
2880       var overlays = this.state.overlays;
2881       for (var i = 0; i < overlays.length; ++i) {
2882         var cur = overlays[i].modeSpec;
2883         if (cur == spec || typeof spec == "string" && cur.name == spec) {
2884           overlays.splice(i, 1);
2885           this.state.modeGen++;
2886           regChange(this);
2887           return;
2888         }
2889       }
2890     }),
2891
2892     indentLine: operation(null, function(n, dir, aggressive) {
2893       if (typeof dir != "string" && typeof dir != "number") {
2894         if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2895         else dir = dir ? "add" : "subtract";
2896       }
2897       if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2898     }),
2899     indentSelection: operation(null, function(how) {
2900       var sel = this.doc.sel;
2901       if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2902       var e = sel.to.line - (sel.to.ch ? 0 : 1);
2903       for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2904     }),
2905
2906     // Fetch the parser token for a given character. Useful for hacks
2907     // that want to inspect the mode state (say, for completion).
2908     getTokenAt: function(pos, precise) {
2909       var doc = this.doc;
2910       pos = clipPos(doc, pos);
2911       var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
2912       var line = getLine(doc, pos.line);
2913       var stream = new StringStream(line.text, this.options.tabSize);
2914       while (stream.pos < pos.ch && !stream.eol()) {
2915         stream.start = stream.pos;
2916         var style = mode.token(stream, state);
2917       }
2918       return {start: stream.start,
2919               end: stream.pos,
2920               string: stream.current(),
2921               className: style || null, // Deprecated, use 'type' instead
2922               type: style || null,
2923               state: state};
2924     },
2925
2926     getTokenTypeAt: function(pos) {
2927       pos = clipPos(this.doc, pos);
2928       var styles = getLineStyles(this, getLine(this.doc, pos.line));
2929       var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
2930       if (ch == 0) return styles[2];
2931       for (;;) {
2932         var mid = (before + after) >> 1;
2933         if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
2934         else if (styles[mid * 2 + 1] < ch) before = mid + 1;
2935         else return styles[mid * 2 + 2];
2936       }
2937     },
2938
2939     getModeAt: function(pos) {
2940       var mode = this.doc.mode;
2941       if (!mode.innerMode) return mode;
2942       return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
2943     },
2944
2945     getHelper: function(pos, type) {
2946       if (!helpers.hasOwnProperty(type)) return;
2947       var help = helpers[type], mode = this.getModeAt(pos);
2948       return mode[type] && help[mode[type]] ||
2949         mode.helperType && help[mode.helperType] ||
2950         help[mode.name];
2951     },
2952
2953     getStateAfter: function(line, precise) {
2954       var doc = this.doc;
2955       line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2956       return getStateBefore(this, line + 1, precise);
2957     },
2958
2959     cursorCoords: function(start, mode) {
2960       var pos, sel = this.doc.sel;
2961       if (start == null) pos = sel.head;
2962       else if (typeof start == "object") pos = clipPos(this.doc, start);
2963       else pos = start ? sel.from : sel.to;
2964       return cursorCoords(this, pos, mode || "page");
2965     },
2966
2967     charCoords: function(pos, mode) {
2968       return charCoords(this, clipPos(this.doc, pos), mode || "page");
2969     },
2970
2971     coordsChar: function(coords, mode) {
2972       coords = fromCoordSystem(this, coords, mode || "page");
2973       return coordsChar(this, coords.left, coords.top);
2974     },
2975
2976     lineAtHeight: function(height, mode) {
2977       height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
2978       return lineAtHeight(this.doc, height + this.display.viewOffset);
2979     },
2980     heightAtLine: function(line, mode) {
2981       var end = false, last = this.doc.first + this.doc.size - 1;
2982       if (line < this.doc.first) line = this.doc.first;
2983       else if (line > last) { line = last; end = true; }
2984       var lineObj = getLine(this.doc, line);
2985       return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
2986         (end ? lineObj.height : 0);
2987     },
2988
2989     defaultTextHeight: function() { return textHeight(this.display); },
2990     defaultCharWidth: function() { return charWidth(this.display); },
2991
2992     setGutterMarker: operation(null, function(line, gutterID, value) {
2993       return changeLine(this, line, function(line) {
2994         var markers = line.gutterMarkers || (line.gutterMarkers = {});
2995         markers[gutterID] = value;
2996         if (!value && isEmpty(markers)) line.gutterMarkers = null;
2997         return true;
2998       });
2999     }),
3000
3001     clearGutter: operation(null, function(gutterID) {
3002       var cm = this, doc = cm.doc, i = doc.first;
3003       doc.iter(function(line) {
3004         if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
3005           line.gutterMarkers[gutterID] = null;
3006           regChange(cm, i, i + 1);
3007           if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
3008         }
3009         ++i;
3010       });
3011     }),
3012
3013     addLineClass: operation(null, function(handle, where, cls) {
3014       return changeLine(this, handle, function(line) {
3015         var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
3016         if (!line[prop]) line[prop] = cls;
3017         else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
3018         else line[prop] += " " + cls;
3019         return true;
3020       });
3021     }),
3022
3023     removeLineClass: operation(null, function(handle, where, cls) {
3024       return changeLine(this, handle, function(line) {
3025         var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
3026         var cur = line[prop];
3027         if (!cur) return false;
3028         else if (cls == null) line[prop] = null;
3029         else {
3030           var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
3031           if (!found) return false;
3032           var end = found.index + found[0].length;
3033           line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
3034         }
3035         return true;
3036       });
3037     }),
3038
3039     addLineWidget: operation(null, function(handle, node, options) {
3040       return addLineWidget(this, handle, node, options);
3041     }),
3042
3043     removeLineWidget: function(widget) { widget.clear(); },
3044
3045     lineInfo: function(line) {
3046       if (typeof line == "number") {
3047         if (!isLine(this.doc, line)) return null;
3048         var n = line;
3049         line = getLine(this.doc, line);
3050         if (!line) return null;
3051       } else {
3052         var n = lineNo(line);
3053         if (n == null) return null;
3054       }
3055       return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
3056               textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
3057               widgets: line.widgets};
3058     },
3059
3060     getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
3061
3062     addWidget: function(pos, node, scroll, vert, horiz) {
3063       var display = this.display;
3064       pos = cursorCoords(this, clipPos(this.doc, pos));
3065       var top = pos.bottom, left = pos.left;
3066       node.style.position = "absolute";
3067       display.sizer.appendChild(node);
3068       if (vert == "over") {
3069         top = pos.top;
3070       } else if (vert == "above" || vert == "near") {
3071         var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
3072         hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
3073         // Default to positioning above (if specified and possible); otherwise default to positioning below
3074         if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
3075           top = pos.top - node.offsetHeight;
3076         else if (pos.bottom + node.offsetHeight <= vspace)
3077           top = pos.bottom;
3078         if (left + node.offsetWidth > hspace)
3079           left = hspace - node.offsetWidth;
3080       }
3081       node.style.top = top + "px";
3082       node.style.left = node.style.right = "";
3083       if (horiz == "right") {
3084         left = display.sizer.clientWidth - node.offsetWidth;
3085         node.style.right = "0px";
3086       } else {
3087         if (horiz == "left") left = 0;
3088         else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
3089         node.style.left = left + "px";
3090       }
3091       if (scroll)
3092         scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
3093     },
3094
3095     triggerOnKeyDown: operation(null, onKeyDown),
3096
3097     execCommand: function(cmd) {return commands[cmd](this);},
3098
3099     findPosH: function(from, amount, unit, visually) {
3100       var dir = 1;
3101       if (amount < 0) { dir = -1; amount = -amount; }
3102       for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
3103         cur = findPosH(this.doc, cur, dir, unit, visually);
3104         if (cur.hitSide) break;
3105       }
3106       return cur;
3107     },
3108
3109     moveH: operation(null, function(dir, unit) {
3110       var sel = this.doc.sel, pos;
3111       if (sel.shift || sel.extend || posEq(sel.from, sel.to))
3112         pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
3113       else
3114         pos = dir < 0 ? sel.from : sel.to;
3115       extendSelection(this.doc, pos, pos, dir);
3116     }),
3117
3118     deleteH: operation(null, function(dir, unit) {
3119       var sel = this.doc.sel;
3120       if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
3121       else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
3122       this.curOp.userSelChange = true;
3123     }),
3124
3125     findPosV: function(from, amount, unit, goalColumn) {
3126       var dir = 1, x = goalColumn;
3127       if (amount < 0) { dir = -1; amount = -amount; }
3128       for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
3129         var coords = cursorCoords(this, cur, "div");
3130         if (x == null) x = coords.left;
3131         else coords.left = x;
3132         cur = findPosV(this, coords, dir, unit);
3133         if (cur.hitSide) break;
3134       }
3135       return cur;
3136     },
3137
3138     moveV: operation(null, function(dir, unit) {
3139       var sel = this.doc.sel;
3140       var pos = cursorCoords(this, sel.head, "div");
3141       if (sel.goalColumn != null) pos.left = sel.goalColumn;
3142       var target = findPosV(this, pos, dir, unit);
3143
3144       if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
3145       extendSelection(this.doc, target, target, dir);
3146       sel.goalColumn = pos.left;
3147     }),
3148
3149     toggleOverwrite: function(value) {
3150       if (value != null && value == this.state.overwrite) return;
3151       if (this.state.overwrite = !this.state.overwrite)
3152         this.display.cursor.className += " CodeMirror-overwrite";
3153       else
3154         this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
3155     },
3156     hasFocus: function() { return this.state.focused; },
3157
3158     scrollTo: operation(null, function(x, y) {
3159       updateScrollPos(this, x, y);
3160     }),
3161     getScrollInfo: function() {
3162       var scroller = this.display.scroller, co = scrollerCutOff;
3163       return {left: scroller.scrollLeft, top: scroller.scrollTop,
3164               height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
3165               clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
3166     },
3167
3168     scrollIntoView: operation(null, function(pos, margin) {
3169       if (typeof pos == "number") pos = Pos(pos, 0);
3170       if (!margin) margin = 0;
3171       var coords = pos;
3172
3173       if (!pos || pos.line != null) {
3174         this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
3175         this.curOp.scrollToPosMargin = margin;
3176         coords = cursorCoords(this, this.curOp.scrollToPos);
3177       }
3178       var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
3179       updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
3180     }),
3181
3182     setSize: operation(null, function(width, height) {
3183       function interpret(val) {
3184         return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
3185       }
3186       if (width != null) this.display.wrapper.style.width = interpret(width);
3187       if (height != null) this.display.wrapper.style.height = interpret(height);
3188       if (this.options.lineWrapping)
3189         this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
3190       this.curOp.forceUpdate = true;
3191     }),
3192
3193     operation: function(f){return runInOp(this, f);},
3194
3195     refresh: operation(null, function() {
3196       var badHeight = this.display.cachedTextHeight == null;
3197       clearCaches(this);
3198       updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
3199       regChange(this);
3200       if (badHeight) estimateLineHeights(this);
3201     }),
3202
3203     swapDoc: operation(null, function(doc) {
3204       var old = this.doc;
3205       old.cm = null;
3206       attachDoc(this, doc);
3207       clearCaches(this);
3208       resetInput(this, true);
3209       updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
3210       return old;
3211     }),
3212
3213     getInputField: function(){return this.display.input;},
3214     getWrapperElement: function(){return this.display.wrapper;},
3215     getScrollerElement: function(){return this.display.scroller;},
3216     getGutterElement: function(){return this.display.gutters;}
3217   };
3218   eventMixin(CodeMirror);
3219
3220   // OPTION DEFAULTS
3221
3222   var optionHandlers = CodeMirror.optionHandlers = {};
3223
3224   // The default configuration options.
3225   var defaults = CodeMirror.defaults = {};
3226
3227   function option(name, deflt, handle, notOnInit) {
3228     CodeMirror.defaults[name] = deflt;
3229     if (handle) optionHandlers[name] =
3230       notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3231   }
3232
3233   var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3234
3235   // These two are, on init, called from the constructor because they
3236   // have to be initialized before the editor can start at all.
3237   option("value", "", function(cm, val) {
3238     cm.setValue(val);
3239   }, true);
3240   option("mode", null, function(cm, val) {
3241     cm.doc.modeOption = val;
3242     loadMode(cm);
3243   }, true);
3244
3245   option("indentUnit", 2, loadMode, true);
3246   option("indentWithTabs", false);
3247   option("smartIndent", true);
3248   option("tabSize", 4, function(cm) {
3249     loadMode(cm);
3250     clearCaches(cm);
3251     regChange(cm);
3252   }, true);
3253   option("electricChars", true);
3254   option("rtlMoveVisually", !windows);
3255
3256   option("theme", "default", function(cm) {
3257     themeChanged(cm);
3258     guttersChanged(cm);
3259   }, true);
3260   option("keyMap", "default", keyMapChanged);
3261   option("extraKeys", null);
3262
3263   option("onKeyEvent", null);
3264   option("onDragEvent", null);
3265
3266   option("lineWrapping", false, wrappingChanged, true);
3267   option("gutters", [], function(cm) {
3268     setGuttersForLineNumbers(cm.options);
3269     guttersChanged(cm);
3270   }, true);
3271   option("fixedGutter", true, function(cm, val) {
3272     cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3273     cm.refresh();
3274   }, true);
3275   option("coverGutterNextToScrollbar", false, updateScrollbars, true);
3276   option("lineNumbers", false, function(cm) {
3277     setGuttersForLineNumbers(cm.options);
3278     guttersChanged(cm);
3279   }, true);
3280   option("firstLineNumber", 1, guttersChanged, true);
3281   option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3282   option("showCursorWhenSelecting", false, updateSelection, true);
3283
3284   option("readOnly", false, function(cm, val) {
3285     if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3286     else if (!val) resetInput(cm, true);
3287   });
3288   option("dragDrop", true);
3289
3290   option("cursorBlinkRate", 530);
3291   option("cursorScrollMargin", 0);
3292   option("cursorHeight", 1);
3293   option("workTime", 100);
3294   option("workDelay", 100);
3295   option("flattenSpans", true);
3296   option("pollInterval", 100);
3297   option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3298   option("historyEventDelay", 500);
3299   option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3300   option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
3301   option("crudeMeasuringFrom", 10000);
3302   option("moveInputWithCursor", true, function(cm, val) {
3303     if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
3304   });
3305
3306   option("tabindex", null, function(cm, val) {
3307     cm.display.input.tabIndex = val || "";
3308   });
3309   option("autofocus", null);
3310
3311   // MODE DEFINITION AND QUERYING
3312
3313   // Known modes, by name and by MIME
3314   var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3315
3316   CodeMirror.defineMode = function(name, mode) {
3317     if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3318     if (arguments.length > 2) {
3319       mode.dependencies = [];
3320       for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3321     }
3322     modes[name] = mode;
3323   };
3324
3325   CodeMirror.defineMIME = function(mime, spec) {
3326     mimeModes[mime] = spec;
3327   };
3328
3329   CodeMirror.resolveMode = function(spec) {
3330     if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
3331       spec = mimeModes[spec];
3332     } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
3333       var found = mimeModes[spec.name];
3334       spec = createObj(found, spec);
3335       spec.name = found.name;
3336     } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
3337       return CodeMirror.resolveMode("application/xml");
3338     }
3339     if (typeof spec == "string") return {name: spec};
3340     else return spec || {name: "null"};
3341   };
3342
3343   CodeMirror.getMode = function(options, spec) {
3344     var spec = CodeMirror.resolveMode(spec);
3345     var mfactory = modes[spec.name];
3346     if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3347     var modeObj = mfactory(options, spec);
3348     if (modeExtensions.hasOwnProperty(spec.name)) {
3349       var exts = modeExtensions[spec.name];
3350       for (var prop in exts) {
3351         if (!exts.hasOwnProperty(prop)) continue;
3352         if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3353         modeObj[prop] = exts[prop];
3354       }
3355     }
3356     modeObj.name = spec.name;
3357
3358     return modeObj;
3359   };
3360
3361   CodeMirror.defineMode("null", function() {
3362     return {token: function(stream) {stream.skipToEnd();}};
3363   });
3364   CodeMirror.defineMIME("text/plain", "null");
3365
3366   var modeExtensions = CodeMirror.modeExtensions = {};
3367   CodeMirror.extendMode = function(mode, properties) {
3368     var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3369     copyObj(properties, exts);
3370   };
3371
3372   // EXTENSIONS
3373
3374   CodeMirror.defineExtension = function(name, func) {
3375     CodeMirror.prototype[name] = func;
3376   };
3377   CodeMirror.defineDocExtension = function(name, func) {
3378     Doc.prototype[name] = func;
3379   };
3380   CodeMirror.defineOption = option;
3381
3382   var initHooks = [];
3383   CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3384
3385   var helpers = CodeMirror.helpers = {};
3386   CodeMirror.registerHelper = function(type, name, value) {
3387     if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
3388     helpers[type][name] = value;
3389   };
3390
3391   // UTILITIES
3392
3393   CodeMirror.isWordChar = isWordChar;
3394
3395   // MODE STATE HANDLING
3396
3397   // Utility functions for working with state. Exported because modes
3398   // sometimes need to do this.
3399   function copyState(mode, state) {
3400     if (state === true) return state;
3401     if (mode.copyState) return mode.copyState(state);
3402     var nstate = {};
3403     for (var n in state) {
3404       var val = state[n];
3405       if (val instanceof Array) val = val.concat([]);
3406       nstate[n] = val;
3407     }
3408     return nstate;
3409   }
3410   CodeMirror.copyState = copyState;
3411
3412   function startState(mode, a1, a2) {
3413     return mode.startState ? mode.startState(a1, a2) : true;
3414   }
3415   CodeMirror.startState = startState;
3416
3417   CodeMirror.innerMode = function(mode, state) {
3418     while (mode.innerMode) {
3419       var info = mode.innerMode(state);
3420       if (!info || info.mode == mode) break;
3421       state = info.state;
3422       mode = info.mode;
3423     }
3424     return info || {mode: mode, state: state};
3425   };
3426
3427   // STANDARD COMMANDS
3428
3429   var commands = CodeMirror.commands = {
3430     selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3431     killLine: function(cm) {
3432       var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3433       if (!sel && cm.getLine(from.line).length == from.ch)
3434         cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3435       else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3436     },
3437     deleteLine: function(cm) {
3438       var l = cm.getCursor().line;
3439       cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3440     },
3441     delLineLeft: function(cm) {
3442       var cur = cm.getCursor();
3443       cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
3444     },
3445     undo: function(cm) {cm.undo();},
3446     redo: function(cm) {cm.redo();},
3447     goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3448     goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3449     goLineStart: function(cm) {
3450       cm.extendSelection(lineStart(cm, cm.getCursor().line));
3451     },
3452     goLineStartSmart: function(cm) {
3453       var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3454       var line = cm.getLineHandle(start.line);
3455       var order = getOrder(line);
3456       if (!order || order[0].level == 0) {
3457         var firstNonWS = Math.max(0, line.text.search(/\S/));
3458         var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3459         cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3460       } else cm.extendSelection(start);
3461     },
3462     goLineEnd: function(cm) {
3463       cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3464     },
3465     goLineRight: function(cm) {
3466       var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3467       cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3468     },
3469     goLineLeft: function(cm) {
3470       var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3471       cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3472     },
3473     goLineUp: function(cm) {cm.moveV(-1, "line");},
3474     goLineDown: function(cm) {cm.moveV(1, "line");},
3475     goPageUp: function(cm) {cm.moveV(-1, "page");},
3476     goPageDown: function(cm) {cm.moveV(1, "page");},
3477     goCharLeft: function(cm) {cm.moveH(-1, "char");},
3478     goCharRight: function(cm) {cm.moveH(1, "char");},
3479     goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3480     goColumnRight: function(cm) {cm.moveH(1, "column");},
3481     goWordLeft: function(cm) {cm.moveH(-1, "word");},
3482     goGroupRight: function(cm) {cm.moveH(1, "group");},
3483     goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3484     goWordRight: function(cm) {cm.moveH(1, "word");},
3485     delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3486     delCharAfter: function(cm) {cm.deleteH(1, "char");},
3487     delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3488     delWordAfter: function(cm) {cm.deleteH(1, "word");},
3489     delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3490     delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3491     indentAuto: function(cm) {cm.indentSelection("smart");},
3492     indentMore: function(cm) {cm.indentSelection("add");},
3493     indentLess: function(cm) {cm.indentSelection("subtract");},
3494     insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3495     defaultTab: function(cm) {
3496       if (cm.somethingSelected()) cm.indentSelection("add");
3497       else cm.replaceSelection("\t", "end", "+input");
3498     },
3499     transposeChars: function(cm) {
3500       var cur = cm.getCursor(), line = cm.getLine(cur.line);
3501       if (cur.ch > 0 && cur.ch < line.length - 1)
3502         cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3503                         Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3504     },
3505     newlineAndIndent: function(cm) {
3506       operation(cm, function() {
3507         cm.replaceSelection("\n", "end", "+input");
3508         cm.indentLine(cm.getCursor().line, null, true);
3509       })();
3510     },
3511     toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3512   };
3513
3514   // STANDARD KEYMAPS
3515
3516   var keyMap = CodeMirror.keyMap = {};
3517   keyMap.basic = {
3518     "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3519     "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3520     "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3521     "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3522   };
3523   // Note that the save and find-related commands aren't defined by
3524   // default. Unknown commands are simply ignored.
3525   keyMap.pcDefault = {
3526     "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3527     "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3528     "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3529     "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3530     "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3531     "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3532     fallthrough: "basic"
3533   };
3534   keyMap.macDefault = {
3535     "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3536     "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3537     "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3538     "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3539     "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3540     "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
3541     fallthrough: ["basic", "emacsy"]
3542   };
3543   keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3544   keyMap.emacsy = {
3545     "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3546     "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3547     "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3548     "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3549   };
3550
3551   // KEYMAP DISPATCH
3552
3553   function getKeyMap(val) {
3554     if (typeof val == "string") return keyMap[val];
3555     else return val;
3556   }
3557
3558   function lookupKey(name, maps, handle) {
3559     function lookup(map) {
3560       map = getKeyMap(map);
3561       var found = map[name];
3562       if (found === false) return "stop";
3563       if (found != null && handle(found)) return true;
3564       if (map.nofallthrough) return "stop";
3565
3566       var fallthrough = map.fallthrough;
3567       if (fallthrough == null) return false;
3568       if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3569         return lookup(fallthrough);
3570       for (var i = 0, e = fallthrough.length; i < e; ++i) {
3571         var done = lookup(fallthrough[i]);
3572         if (done) return done;
3573       }
3574       return false;
3575     }
3576
3577     for (var i = 0; i < maps.length; ++i) {
3578       var done = lookup(maps[i]);
3579       if (done) return done != "stop";
3580     }
3581   }
3582   function isModifierKey(event) {
3583     var name = keyNames[event.keyCode];
3584     return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3585   }
3586   function keyName(event, noShift) {
3587     if (opera && event.keyCode == 34 && event["char"]) return false;
3588     var name = keyNames[event.keyCode];
3589     if (name == null || event.altGraphKey) return false;
3590     if (event.altKey) name = "Alt-" + name;
3591     if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3592     if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3593     if (!noShift && event.shiftKey) name = "Shift-" + name;
3594     return name;
3595   }
3596   CodeMirror.lookupKey = lookupKey;
3597   CodeMirror.isModifierKey = isModifierKey;
3598   CodeMirror.keyName = keyName;
3599
3600   // FROMTEXTAREA
3601
3602   CodeMirror.fromTextArea = function(textarea, options) {
3603     if (!options) options = {};
3604     options.value = textarea.value;
3605     if (!options.tabindex && textarea.tabindex)
3606       options.tabindex = textarea.tabindex;
3607     if (!options.placeholder && textarea.placeholder)
3608       options.placeholder = textarea.placeholder;
3609     // Set autofocus to true if this textarea is focused, or if it has
3610     // autofocus and no other element is focused.
3611     if (options.autofocus == null) {
3612       var hasFocus = document.body;
3613       // doc.activeElement occasionally throws on IE
3614       try { hasFocus = document.activeElement; } catch(e) {}
3615       options.autofocus = hasFocus == textarea ||
3616         textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3617     }
3618
3619     function save() {textarea.value = cm.getValue();}
3620     if (textarea.form) {
3621       on(textarea.form, "submit", save);
3622       // Deplorable hack to make the submit method do the right thing.