Вход Регистрация
Файл: tyde/www/web/js/codemirror.js
Строк: 13372
<?php
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// This is CodeMirror (http://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
// You can find some technical background for some of the code below
// at http://marijnhaverbeke.nl/blog/#cm-internals .

(function(mod) {
  if (
typeof exports == "object" && typeof module == "object"// CommonJS
    
module.exports mod();
  else if (
typeof define == "function" && define.amd// AMD
    
return define([], mod);
  else 
// Plain browser env
    
this.CodeMirror mod();
})(function() {
  
"use strict";

  
// BROWSER SNIFFING

  // Kludges for bugs and behavior differences that can't be feature
  // detected are enabled based on userAgent etc sniffing.

  
var gecko = /gecko/d/i.test(navigator.userAgent);
  
// ie_uptoN means Internet Explorer version N or lower
  
var ie_upto10 = /MSIE d/.test(navigator.userAgent);
  var 
ie_upto7 ie_upto10 && (document.documentMode == null || document.documentMode 8);
  var 
ie_upto8 ie_upto10 && (document.documentMode == null || document.documentMode 9);
  var 
ie_upto9 ie_upto10 && (document.documentMode == null || document.documentMode 10);
  var 
ie_11up = /Trident/([7-9]|d{2,})./.test(navigator.userAgent);
  var 
ie ie_upto10 || ie_11up;
  var 
webkit = /WebKit//.test(navigator.userAgent);
  
var qtwebkit webkit && /Qt/d+.d+/.test(navigator.userAgent);
  var 
chrome = /Chrome//.test(navigator.userAgent);
  
var presto = /Opera//.test(navigator.userAgent);
  
var safari = /Apple Computer/.test(navigator.vendor);
  var 
khtml = /KHTML//.test(navigator.userAgent);
  
var mac_geMountainLion = /Mac OS X 1dD([8-9]|dd)D/.test(navigator.userAgent);
  var 
phantom = /PhantomJS/.test(navigator.userAgent);

  var 
ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile/w+/.test(navigator.userAgent);
  
// This is woefully incomplete. Suggestions for alternative methods welcome.
  
var mobile ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
  var 
mac ios || /Mac/.test(navigator.platform);
  var 
windows = /win/i.test(navigator.platform);

  var 
presto_version presto && navigator.userAgent.match(/Version/(d*.d*)/);
  if (
presto_versionpresto_version Number(presto_version[1]);
  if (
presto_version && presto_version >= 15) { presto falsewebkit true; }
  
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
  
var flipCtrlCmd mac && (qtwebkit || presto && (presto_version == null || presto_version 12.11));
  var 
captureRightClick gecko || (ie && !ie_upto8);

  
// Optimize some code when these features are not used.
  
var sawReadOnlySpans falsesawCollapsedSpans false;

  
// EDITOR CONSTRUCTOR

  // A CodeMirror instance represents an editor. This is the object
  // that user code is usually dealing with.

  
function CodeMirror(placeoptions) {
    if (!(
this instanceof CodeMirror)) return new CodeMirror(placeoptions);

    
this.options options options || {};
    
// Determine effective options based on given values and defaults.
    
copyObj(defaultsoptionsfalse);
    
setGuttersForLineNumbers(options);

    var 
doc options.value;
    if (
typeof doc == "string"doc = new Doc(docoptions.mode);
    
this.doc doc;

    var 
display this.display = new Display(placedoc);
    
display.wrapper.CodeMirror this;
    
updateGutters(this);
    
themeChanged(this);
    if (
options.lineWrapping)
      
this.display.wrapper.className += " CodeMirror-wrap";
    if (
options.autofocus && !mobilefocusInput(this);

    
this.state = {
      
keyMaps: [],  // stores maps added by addKeyMap
      
overlays: [], // highlighting overlays, as added by addOverlay
      
modeGen0,   // bumped when mode/overlay changes, used to invalidate highlighting info
      
overwritefalsefocusedfalse,
      
suppressEditsfalse// used to disable editing during key handlers when in readOnly mode
      
pasteIncomingfalsecutIncomingfalse// help recognize paste/cut edits in readInput
      
draggingTextfalse,
      
highlight: new Delayed() // stores highlight worker timeout
    
};

    
// Override magic textarea content restore that IE sometimes does
    // on our hidden textarea on reload
    
if (ie_upto10setTimeout(bind(resetInputthistrue), 20);

    
registerEventHandlers(this);
    
ensureGlobalHandlers();

    var 
cm this;
    
runInOp(this, function() {
      
cm.curOp.forceUpdate true;
      
attachDoc(cmdoc);

      if ((
options.autofocus && !mobile) || activeElt() == display.input)
        
setTimeout(bind(onFocuscm), 20);
      else
        
onBlur(cm);

      for (var 
opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
        
optionHandlers[opt](cmoptions[opt], Init);
      for (var 
0initHooks.length; ++iinitHooks[i](cm);
    });
  }

  
// DISPLAY CONSTRUCTOR

  // The display handles the DOM integration, both for input reading
  // and content drawing. It holds references to DOM nodes and
  // display-related state.

  
function Display(placedoc) {
    var 
this;

    
// The semihidden textarea that is focused when the editor is
    // focused, and receives input.
    
var input d.input elt("textarea"nullnull"position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
    
// The textarea is kept positioned near the cursor to prevent the
    // fact that it'll be scrolled into view on input from scrolling
    // our fake cursor out of view. On webkit, when wrap=off, paste is
    // very slow. So make the area wide instead.
    
if (webkitinput.style.width "1000px";
    else 
input.setAttribute("wrap""off");
    
// If border: 0; -- iOS fails to open keyboard (issue #1287)
    
if (iosinput.style.border "1px solid black";
    
input.setAttribute("autocorrect""off"); input.setAttribute("autocapitalize""off"); input.setAttribute("spellcheck""false");

    
// Wraps and hides input textarea
    
d.inputDiv elt("div", [input], null"overflow: hidden; position: relative; width: 3px; height: 0px;");
    
// The fake scrollbar elements.
    
d.scrollbarH elt("div", [elt("div"nullnull"height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
    
d.scrollbarV elt("div", [elt("div"nullnull"min-width: 1px")], "CodeMirror-vscrollbar");
    
// Covers bottom-right square when both scrollbars are present.
    
d.scrollbarFiller elt("div"null"CodeMirror-scrollbar-filler");
    
// Covers bottom of gutter when coverGutterNextToScrollbar is on
    // and h scrollbar is present.
    
d.gutterFiller elt("div"null"CodeMirror-gutter-filler");
    
// Will contain the actual code, positioned to cover the viewport.
    
d.lineDiv elt("div"null"CodeMirror-code");
    
// Elements are added to these to represent selection and cursors.
    
d.selectionDiv elt("div"nullnull"position: relative; z-index: 1");
    
d.cursorDiv elt("div"null"CodeMirror-cursors");
    
// A visibility: hidden element used to find the size of things.
    
d.measure elt("div"null"CodeMirror-measure");
    
// When lines outside of the viewport are measured, they are drawn in this.
    
d.lineMeasure elt("div"null"CodeMirror-measure");
    
// Wraps everything that needs to exist inside the vertically-padded coordinate system
    
d.lineSpace elt("div", [d.measured.lineMeasured.selectionDivd.cursorDivd.lineDiv],
                      
null"position: relative; outline: none");
    
// Moved around its parent to cover visible view.
    
d.mover elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null"position: relative");
    
// Set to the height of the document, allowing scrolling.
    
d.sizer elt("div", [d.mover], "CodeMirror-sizer");
    
// Behavior of elts with overflow: auto and padding is
    // inconsistent across browsers. This is used to ensure the
    // scrollable area is big enough.
    
d.heightForcer elt("div"nullnull"position: absolute; height: " scrollerCutOff "px; width: 1px;");
    
// Will contain the gutters, if any.
    
d.gutters elt("div"null"CodeMirror-gutters");
    
d.lineGutter null;
    
// Actual scrollable element.
    
d.scroller elt("div", [d.sizerd.heightForcerd.gutters], "CodeMirror-scroll");
    
d.scroller.setAttribute("tabIndex""-1");
    
// The element in which the editor lives.
    
d.wrapper elt("div", [d.inputDivd.scrollbarHd.scrollbarV,
                            
d.scrollbarFillerd.gutterFillerd.scroller], "CodeMirror");

    
// Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
    
if (ie_upto7) { d.gutters.style.zIndex = -1d.scroller.style.paddingRight 0; }
    
// Needed to hide big blue blinking cursor on Mobile Safari
    
if (iosinput.style.width "0px";
    if (!
webkitd.scroller.draggable true;
    
// Needed to handle Tab key in KHTML
    
if (khtml) { d.inputDiv.style.height "1px"d.inputDiv.style.position "absolute"; }
    
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
    
if (ie_upto7d.scrollbarH.style.minHeight d.scrollbarV.style.minWidth "18px";

    if (
place.appendChildplace.appendChild(d.wrapper);
    else 
place(d.wrapper);

    
// Current rendered range (may be bigger than the view window).
    
d.viewFrom d.viewTo doc.first;
    
// Information about the rendered lines.
    
d.view = [];
    
// Holds info about a single rendered line when it was rendered
    // for measurement, while not in view.
    
d.externalMeasured null;
    
// Empty space (in pixels) above the view
    
d.viewOffset 0;
    
d.lastSizeC 0;
    
d.updateLineNumbers null;

    
// Used to only resize the line number gutter when necessary (when
    // the amount of lines crosses a boundary that makes its width change)
    
d.lineNumWidth d.lineNumInnerWidth d.lineNumChars null;
    
// See readInput and resetInput
    
d.prevInput "";
    
// Set to true when a non-horizontal-scrolling line widget is
    // added. As an optimization, line widget aligning is skipped when
    // this is false.
    
d.alignWidgets false;
    
// Flag that indicates whether we expect input to appear real soon
    // now (after some event like 'keypress' or 'input') and are
    // polling intensively.
    
d.pollingFast false;
    
// Self-resetting timeout for the poller
    
d.poll = new Delayed();

    
d.cachedCharWidth d.cachedTextHeight d.cachedPaddingH null;

    
// Tracks when resetInput has punted to just putting a short
    // string into the textarea instead of the full selection.
    
d.inaccurateSelection false;

    
// Tracks the maximum line length so that the horizontal scrollbar
    // can be kept static when scrolling.
    
d.maxLine null;
    
d.maxLineLength 0;
    
d.maxLineChanged false;

    
// Used for measuring wheel scrolling granularity
    
d.wheelDX d.wheelDY d.wheelStartX d.wheelStartY null;

    
// True when shift is held down.
    
d.shift false;

    
// Used to track whether anything happened since the context menu
    // was opened.
    
d.selForContextMenu null;
  }

  
// STATE UPDATES

  // Used to get the editor into a consistent state again when options change.

  
function loadMode(cm) {
    
cm.doc.mode CodeMirror.getMode(cm.optionscm.doc.modeOption);
    
resetModeState(cm);
  }

  function 
resetModeState(cm) {
    
cm.doc.iter(function(line) {
      if (
line.stateAfterline.stateAfter null;
      if (
line.stylesline.styles null;
    });
    
cm.doc.frontier cm.doc.first;
    
startWorker(cm100);
    
cm.state.modeGen++;
    if (
cm.curOpregChange(cm);
  }

  function 
wrappingChanged(cm) {
    if (
cm.options.lineWrapping) {
      
addClass(cm.display.wrapper"CodeMirror-wrap");
      
cm.display.sizer.style.minWidth "";
    } else {
      
rmClass(cm.display.wrapper"CodeMirror-wrap");
      
findMaxLine(cm);
    }
    
estimateLineHeights(cm);
    
regChange(cm);
    
clearCaches(cm);
    
setTimeout(function(){updateScrollbars(cm);}, 100);
  }

  
// Returns a function that estimates the height of a line, to use as
  // first approximation until the line becomes visible (and is thus
  // properly measurable).
  
function estimateHeight(cm) {
    var 
th textHeight(cm.display), wrapping cm.options.lineWrapping;
    var 
perLine wrapping && Math.max(5cm.display.scroller.clientWidth charWidth(cm.display) - 3);
    return function(
line) {
      if (
lineIsHidden(cm.docline)) return 0;

      var 
widgetsHeight 0;
      if (
line.widgets) for (var 0line.widgets.lengthi++) {
        if (
line.widgets[i].heightwidgetsHeight += line.widgets[i].height;
      }

      if (
wrapping)
        return 
widgetsHeight + (Math.ceil(line.text.length perLine) || 1) * th;
      else
        return 
widgetsHeight th;
    };
  }

  function 
estimateLineHeights(cm) {
    var 
doc cm.docest estimateHeight(cm);
    
doc.iter(function(line) {
      var 
estHeight est(line);
      if (
estHeight != line.heightupdateLineHeight(lineestHeight);
    });
  }

  function 
keyMapChanged(cm) {
    var 
map keyMap[cm.options.keyMap], style map.style;
    
cm.display.wrapper.className cm.display.wrapper.className.replace(/s*cm-keymap-S+/g"") +
      (
style " cm-keymap-" style "");
  }

  function 
themeChanged(cm) {
    
cm.display.wrapper.className cm.display.wrapper.className.replace(/s*cm-s-S+/g"") +
      
cm.options.theme.replace(/(^|s)s*/g" cm-s-");
    
clearCaches(cm);
  }

  function 
guttersChanged(cm) {
    
updateGutters(cm);
    
regChange(cm);
    
setTimeout(function(){alignHorizontally(cm);}, 20);
  }

  
// Rebuild the gutter elements, ensure the margin to the left of the
  // code matches their width.
  
function updateGutters(cm) {
    var 
gutters cm.display.guttersspecs cm.options.gutters;
    
removeChildren(gutters);
    for (var 
0specs.length; ++i) {
      var 
gutterClass specs[i];
      var 
gElt gutters.appendChild(elt("div"null"CodeMirror-gutter " gutterClass));
      if (
gutterClass == "CodeMirror-linenumbers") {
        
cm.display.lineGutter gElt;
        
gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
      }
    }
    
gutters.style.display "" "none";
    
updateGutterSpace(cm);
  }

  function 
updateGutterSpace(cm) {
    var 
width cm.display.gutters.offsetWidth;
    
cm.display.sizer.style.marginLeft width "px";
    
cm.display.scrollbarH.style.left cm.options.fixedGutter width "px" 0;
  }

  
// Compute the character length of a line, taking into account
  // collapsed ranges (see markText) that might hide parts, and join
  // other lines onto it.
  
function lineLength(line) {
    if (
line.height == 0) return 0;
    var 
len line.text.lengthmergedcur line;
    while (
merged collapsedSpanAtStart(cur)) {
      var 
found merged.find(0true);
      
cur found.from.line;
      
len += found.from.ch found.to.ch;
    }
    
cur line;
    while (
merged collapsedSpanAtEnd(cur)) {
      var 
found merged.find(0true);
      
len -= cur.text.length found.from.ch;
      
cur found.to.line;
      
len += cur.text.length found.to.ch;
    }
    return 
len;
  }

  
// Find the longest line in the document.
  
function findMaxLine(cm) {
    var 
cm.displaydoc cm.doc;
    
d.maxLine getLine(docdoc.first);
    
d.maxLineLength lineLength(d.maxLine);
    
d.maxLineChanged true;
    
doc.iter(function(line) {
      var 
len lineLength(line);
      if (
len d.maxLineLength) {
        
d.maxLineLength len;
        
d.maxLine line;
      }
    });
  }

  
// Make sure the gutters options contains the element
  // "CodeMirror-linenumbers" when the lineNumbers option is true.
  
function setGuttersForLineNumbers(options) {
    var 
found indexOf(options.gutters"CodeMirror-linenumbers");
    if (
found == -&& options.lineNumbers) {
      
options.gutters options.gutters.concat(["CodeMirror-linenumbers"]);
    } else if (
found > -&& !options.lineNumbers) {
      
options.gutters options.gutters.slice(0);
      
options.gutters.splice(found1);
    }
  }

  
// SCROLLBARS

  // Prepare DOM reads needed to update the scrollbars. Done in one
  // shot to minimize update/measure roundtrips.
  
function measureForScrollbars(cm) {
    var 
scroll cm.display.scroller;
    return {
      
clientHeightscroll.clientHeight,
      
barHeightcm.display.scrollbarV.clientHeight,
      
scrollWidthscroll.scrollWidthclientWidthscroll.clientWidth,
      
barWidthcm.display.scrollbarH.clientWidth,
      
docHeightMath.round(cm.doc.height paddingVert(cm.display))
    };
  }

  
// Re-synchronize the fake scrollbars with the actual size of the
  // content.
  
function updateScrollbars(cmmeasure) {
    if (!
measuremeasure measureForScrollbars(cm);
    var 
cm.display;
    var 
scrollHeight measure.docHeight scrollerCutOff;
    var 
needsH measure.scrollWidth measure.clientWidth;
    var 
needsV scrollHeight measure.clientHeight;
    if (
needsV) {
      
d.scrollbarV.style.display "block";
      
d.scrollbarV.style.bottom needsH scrollbarWidth(d.measure) + "px" "0";
      
// A bug in IE8 can cause this value to be negative, so guard it.
      
d.scrollbarV.firstChild.style.height =
        
Math.max(0scrollHeight measure.clientHeight + (measure.barHeight || d.scrollbarV.clientHeight)) + "px";
    } else {
      
d.scrollbarV.style.display "";
      
d.scrollbarV.firstChild.style.height "0";
    }
    if (
needsH) {
      
d.scrollbarH.style.display "block";
      
d.scrollbarH.style.right needsV scrollbarWidth(d.measure) + "px" "0";
      
d.scrollbarH.firstChild.style.width =
        (
measure.scrollWidth measure.clientWidth + (measure.barWidth || d.scrollbarH.clientWidth)) + "px";
    } else {
      
d.scrollbarH.style.display "";
      
d.scrollbarH.firstChild.style.width "0";
    }
    if (
needsH && needsV) {
      
d.scrollbarFiller.style.display "block";
      
d.scrollbarFiller.style.height d.scrollbarFiller.style.width scrollbarWidth(d.measure) + "px";
    } else 
d.scrollbarFiller.style.display "";
    if (
needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
      
d.gutterFiller.style.display "block";
      
d.gutterFiller.style.height scrollbarWidth(d.measure) + "px";
      
d.gutterFiller.style.width d.gutters.offsetWidth "px";
    } else 
d.gutterFiller.style.display "";

    if (!
cm.state.checkedOverlayScrollbar && measure.clientHeight 0) {
      if (
scrollbarWidth(d.measure) === 0) {
        var 
mac && !mac_geMountainLion "12px" "18px";
        
d.scrollbarV.style.minWidth d.scrollbarH.style.minHeight w;
        var 
barMouseDown = function(e) {
          if (
e_target(e) != d.scrollbarV && e_target(e) != d.scrollbarH)
            
operation(cmonMouseDown)(e);
        };
        
on(d.scrollbarV"mousedown"barMouseDown);
        
on(d.scrollbarH"mousedown"barMouseDown);
      }
      
cm.state.checkedOverlayScrollbar true;
    }
  }

  
// Compute the lines that are visible in a given viewport (defaults
  // the the current scroll position). viewPort may contain top,
  // height, and ensure (see op.scrollToPos) properties.
  
function visibleLines(displaydocviewPort) {
    var 
top viewPort && viewPort.top != null Math.max(0viewPort.top) : display.scroller.scrollTop;
    
top Math.floor(top paddingTop(display));
    var 
bottom viewPort && viewPort.bottom != null viewPort.bottom top display.wrapper.clientHeight;

    var 
from lineAtHeight(doctop), to lineAtHeight(docbottom);
    
// Ensure is a {from: {line, ch}, to: {line, ch}} object, and
    // forces those lines into the viewport (if possible).
    
if (viewPort && viewPort.ensure) {
      var 
ensureFrom viewPort.ensure.from.lineensureTo viewPort.ensure.to.line;
      if (
ensureFrom from)
        return {
fromensureFrom,
                
tolineAtHeight(docheightAtLine(getLine(docensureFrom)) + display.wrapper.clientHeight)};
      if (
Math.min(ensureTodoc.lastLine()) >= to)
        return {
fromlineAtHeight(docheightAtLine(getLine(docensureTo)) - display.wrapper.clientHeight),
                
toensureTo};
    }
    return {
fromfromtoMath.max(tofrom 1)};
  }

  
// LINE NUMBERS

  // Re-align line numbers and gutter marks to compensate for
  // horizontal scrolling.
  
function alignHorizontally(cm) {
    var 
display cm.displayview display.view;
    if (!
display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
    var 
comp compensateForHScroll(display) - display.scroller.scrollLeft cm.doc.scrollLeft;
    var 
gutterW display.gutters.offsetWidthleft comp "px";
    for (var 
0view.lengthi++) if (!view[i].hidden) {
      if (
cm.options.fixedGutter && view[i].gutter)
        
view[i].gutter.style.left left;
      var 
align view[i].alignable;
      if (
align) for (var 0align.lengthj++)
        
align[j].style.left left;
    }
    if (
cm.options.fixedGutter)
      
display.gutters.style.left = (comp gutterW) + "px";
  }

  
// Used to ensure that the line number gutter is still the right
  // size for the current document size. Returns true when an update
  // is needed.
  
function maybeUpdateLineNumberWidth(cm) {
    if (!
cm.options.lineNumbers) return false;
    var 
doc cm.doclast lineNumberFor(cm.optionsdoc.first doc.size 1), display cm.display;
    if (
last.length != display.lineNumChars) {
      var 
test display.measure.appendChild(elt("div", [elt("div"last)],
                                                 
"CodeMirror-linenumber CodeMirror-gutter-elt"));
      var 
innerW test.firstChild.offsetWidthpadding test.offsetWidth innerW;
      
display.lineGutter.style.width "";
      
display.lineNumInnerWidth Math.max(innerWdisplay.lineGutter.offsetWidth padding);
      
display.lineNumWidth display.lineNumInnerWidth padding;
      
display.lineNumChars display.lineNumInnerWidth last.length : -1;
      
display.lineGutter.style.width display.lineNumWidth "px";
      
updateGutterSpace(cm);
      return 
true;
    }
    return 
false;
  }

  function 
lineNumberFor(optionsi) {
    return 
String(options.lineNumberFormatter(options.firstLineNumber));
  }

  
// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
  // but using getBoundingClientRect to get a sub-pixel-accurate
  // result.
  
function compensateForHScroll(display) {
    return 
display.scroller.getBoundingClientRect().left display.sizer.getBoundingClientRect().left;
  }

  
// DISPLAY DRAWING

  // Updates the display, selection, and scrollbars, using the
  // information in display.view to find out which nodes are no longer
  // up-to-date. Tries to bail out early when no changes are needed,
  // unless forced is true.
  // Returns true if an actual update happened, false otherwise.
  
function updateDisplay(cmviewPortforced) {
    var 
oldFrom cm.display.viewFromoldTo cm.display.viewToupdated;
    var 
visible visibleLines(cm.displaycm.docviewPort);
    for (var 
first true;; first false) {
      var 
oldWidth cm.display.scroller.clientWidth;
      if (!
updateDisplayInner(cmvisibleforced)) break;
      
updated true;

      
// If the max line changed since it was last measured, measure it,
      // and ensure the document's width matches it.
      
if (cm.display.maxLineChanged && !cm.options.lineWrapping)
        
adjustContentWidth(cm);

      var 
barMeasure measureForScrollbars(cm);
      
updateSelection(cm);
      
setDocumentHeight(cmbarMeasure);
      
updateScrollbars(cmbarMeasure);
      if (
webkit && cm.options.lineWrapping)
        
checkForWebkitWidthBug(cmbarMeasure); // (Issue #2420)
      
if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
        
forced true;
        continue;
      }
      
forced false;

      
// Clip forced viewport to actual scrollable area.
      
if (viewPort && viewPort.top != null)
        
viewPort = {topMath.min(barMeasure.docHeight scrollerCutOff barMeasure.clientHeightviewPort.top)};
      
// Updated line heights might result in the drawn area not
      // actually covering the viewport. Keep looping until it does.
      
visible visibleLines(cm.displaycm.docviewPort);
      if (
visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo)
        break;
    }

    
cm.display.updateLineNumbers null;
    if (
updated) {
      
signalLater(cm"update"cm);
      if (
cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo)
        
signalLater(cm"viewportChange"cmcm.display.viewFromcm.display.viewTo);
    }
    return 
updated;
  }

  
// Does the actual updating of the line display. Bails out
  // (returning false) when there is nothing to be done and forced is
  // false.
  
function updateDisplayInner(cmvisibleforced) {
    var 
display cm.displaydoc cm.doc;
    if (!
display.wrapper.offsetWidth) {
      
resetView(cm);
      return;
    }

    
// Bail out if the visible area is already rendered and nothing changed.
    
if (!forced && visible.from >= display.viewFrom && visible.to <= display.viewTo &&
        
countDirtyView(cm) == 0)
      return;

    if (
maybeUpdateLineNumberWidth(cm))
      
resetView(cm);
    var 
dims getDimensions(cm);

    
// Compute a suitable new viewport (from & to)
    
var end doc.first doc.size;
    var 
from Math.max(visible.from cm.options.viewportMargindoc.first);
    var 
to Math.min(endvisible.to cm.options.viewportMargin);
    if (
display.viewFrom from && from display.viewFrom 20from Math.max(doc.firstdisplay.viewFrom);
    if (
display.viewTo to && display.viewTo to 20to Math.min(enddisplay.viewTo);
    if (
sawCollapsedSpans) {
      
from visualLineNo(cm.docfrom);
      
to visualLineEndNo(cm.docto);
    }

    var 
different from != display.viewFrom || to != display.viewTo ||
      
display.lastSizeC != display.wrapper.clientHeight;
    
adjustView(cmfromto);

    
display.viewOffset heightAtLine(getLine(cm.docdisplay.viewFrom));
    
// Position the mover div to align with the current scroll position
    
cm.display.mover.style.top display.viewOffset "px";

    var 
toUpdate countDirtyView(cm);
    if (!
different && toUpdate == && !forced) return;

    
// For big changes, we hide the enclosing element during the
    // update, since that speeds up the operations on most browsers.
    
var focused activeElt();
    if (
toUpdate 4display.lineDiv.style.display "none";
    
patchDisplay(cmdisplay.updateLineNumbersdims);
    if (
toUpdate 4display.lineDiv.style.display "";
    
// There might have been a widget with a focused element that got
    // hidden or updated, if so re-focus it.
    
if (focused && activeElt() != focused && focused.offsetHeightfocused.focus();

    
// Prevent selection and cursors from interfering with the scroll
    // width.
    
removeChildren(display.cursorDiv);
    
removeChildren(display.selectionDiv);

    if (
different) {
      
display.lastSizeC display.wrapper.clientHeight;
      
startWorker(cm400);
    }

    
updateHeightsInViewport(cm);

    return 
true;
  }

  function 
adjustContentWidth(cm) {
    var 
display cm.display;
    var 
width measureChar(cmdisplay.maxLinedisplay.maxLine.text.length).left;
    
display.maxLineChanged false;
    var 
minWidth Math.max(0width 3);
    var 
maxScrollLeft Math.max(0display.sizer.offsetLeft minWidth scrollerCutOff display.scroller.clientWidth);
    
display.sizer.style.minWidth minWidth "px";
    if (
maxScrollLeft cm.doc.scrollLeft)
      
setScrollLeft(cmMath.min(display.scroller.scrollLeftmaxScrollLeft), true);
  }

  function 
setDocumentHeight(cmmeasure) {
    
cm.display.sizer.style.minHeight cm.display.heightForcer.style.top measure.docHeight "px";
    
cm.display.gutters.style.height Math.max(measure.docHeightmeasure.clientHeight scrollerCutOff) + "px";
  }

  function 
checkForWebkitWidthBug(cmmeasure) {
    
// Work around Webkit bug where it sometimes reserves space for a
    // non-existing phantom scrollbar in the scroller (Issue #2420)
    
if (cm.display.sizer.offsetWidth cm.display.gutters.offsetWidth cm.display.scroller.clientWidth 1) {
      
cm.display.sizer.style.minHeight cm.display.heightForcer.style.top "0px";
      
cm.display.gutters.style.height measure.docHeight "px";
    }
  }

  
// Read the actual heights of the rendered lines, and update their
  // stored heights to match.
  
function updateHeightsInViewport(cm) {
    var 
display cm.display;
    var 
prevBottom display.lineDiv.offsetTop;
    for (var 
0display.view.lengthi++) {
      var 
cur display.view[i], height;
      if (
cur.hidden) continue;
      if (
ie_upto7) {
        var 
bot cur.node.offsetTop cur.node.offsetHeight;
        
height bot prevBottom;
        
prevBottom bot;
      } else {
        var 
box cur.node.getBoundingClientRect();
        
height box.bottom box.top;
      }
      var 
diff cur.line.height height;
      if (
height 2height textHeight(display);
      if (
diff .001 || diff < -.001) {
        
updateLineHeight(cur.lineheight);
        
updateWidgetHeight(cur.line);
        if (
cur.rest) for (var 0cur.rest.lengthj++)
          
updateWidgetHeight(cur.rest[j]);
      }
    }
  }

  
// Read and store the height of line widgets associated with the
  // given line.
  
function updateWidgetHeight(line) {
    if (
line.widgets) for (var 0line.widgets.length; ++i)
      
line.widgets[i].height line.widgets[i].node.offsetHeight;
  }

  
// Do a bulk-read of the DOM positions and sizes needed to draw the
  // view, so that we don't interleave reading and writing to the DOM.
  
function getDimensions(cm) {
    var 
cm.displayleft = {}, width = {};
    for (var 
d.gutters.firstChild0nn.nextSibling, ++i) {
      
left[cm.options.gutters[i]] = n.offsetLeft;
      
width[cm.options.gutters[i]] = n.offsetWidth;
    }
    return {
fixedPoscompensateForHScroll(d),
            
gutterTotalWidthd.gutters.offsetWidth,
            
gutterLeftleft,
            
gutterWidthwidth,
            
wrapperWidthd.wrapper.clientWidth};
  }

  
// Sync the actual display DOM structure with display.view, removing
  // nodes for lines that are no longer in view, and creating the ones
  // that are not there yet, and updating the ones that are out of
  // date.
  
function patchDisplay(cmupdateNumbersFromdims) {
    var 
display cm.displaylineNumbers cm.options.lineNumbers;
    var 
container display.lineDivcur container.firstChild;

    function 
rm(node) {
      var 
next node.nextSibling;
      
// Works around a throw-scroll bug in OS X Webkit
      
if (webkit && mac && cm.display.currentWheelTarget == node)
        
node.style.display "none";
      else
        
node.parentNode.removeChild(node);
      return 
next;
    }

    var 
view display.viewlineN display.viewFrom;
    
// Loop over the elements in the view, syncing cur (the DOM nodes
    // in display.lineDiv) with the view as we go.
    
for (var 0view.lengthi++) {
      var 
lineView view[i];
      if (
lineView.hidden) {
      } else if (!
lineView.node) { // Not drawn yet
        
var node buildLineElement(cmlineViewlineNdims);
        
container.insertBefore(nodecur);
      } else { 
// Already drawn
        
while (cur != lineView.nodecur rm(cur);
        var 
updateNumber lineNumbers && updateNumbersFrom != null &&
          
updateNumbersFrom <= lineN && lineView.lineNumber;
        if (
lineView.changes) {
          if (
indexOf(lineView.changes"gutter") > -1updateNumber false;
          
updateLineForChanges(cmlineViewlineNdims);
        }
        if (
updateNumber) {
          
removeChildren(lineView.lineNumber);
          
lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.optionslineN)));
        }
        
cur lineView.node.nextSibling;
      }
      
lineN += lineView.size;
    }
    while (
curcur rm(cur);
  }

  
// When an aspect of a line changes, a string is added to
  // lineView.changes. This updates the relevant part of the line's
  // DOM structure.
  
function updateLineForChanges(cmlineViewlineNdims) {
    for (var 
0lineView.changes.lengthj++) {
      var 
type lineView.changes[j];
      if (
type == "text"updateLineText(cmlineView);
      else if (
type == "gutter"updateLineGutter(cmlineViewlineNdims);
      else if (
type == "class"updateLineClasses(lineView);
      else if (
type == "widget"updateLineWidgets(lineViewdims);
    }
    
lineView.changes null;
  }

  
// Lines with gutter elements, widgets or a background class need to
  // be wrapped, and have the extra elements added to the wrapper div
  
function ensureLineWrapped(lineView) {
    if (
lineView.node == lineView.text) {
      
lineView.node elt("div"nullnull"position: relative");
      if (
lineView.text.parentNode)
        
lineView.text.parentNode.replaceChild(lineView.nodelineView.text);
      
lineView.node.appendChild(lineView.text);
      if (
ie_upto7lineView.node.style.zIndex 2;
    }
    return 
lineView.node;
  }

  function 
updateLineBackground(lineView) {
    var 
cls lineView.bgClass lineView.bgClass " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
    if (
clscls += " CodeMirror-linebackground";
    if (
lineView.background) {
      if (
clslineView.background.className cls;
      else { 
lineView.background.parentNode.removeChild(lineView.background); lineView.background null; }
    } else if (
cls) {
      var 
wrap ensureLineWrapped(lineView);
      
lineView.background wrap.insertBefore(elt("div"nullcls), wrap.firstChild);
    }
  }

  
// Wrapper around buildLineContent which will reuse the structure
  // in display.externalMeasured when possible.
  
function getLineContent(cmlineView) {
    var 
ext cm.display.externalMeasured;
    if (
ext && ext.line == lineView.line) {
      
cm.display.externalMeasured null;
      
lineView.measure ext.measure;
      return 
ext.built;
    }
    return 
buildLineContent(cmlineView);
  }

  
// Redraw the line's text. Interacts with the background and text
  // classes because the mode may output tokens that influence these
  // classes.
  
function updateLineText(cmlineView) {
    var 
cls lineView.text.className;
    var 
built getLineContent(cmlineView);
    if (
lineView.text == lineView.nodelineView.node built.pre;
    
lineView.text.parentNode.replaceChild(built.prelineView.text);
    
lineView.text built.pre;
    if (
built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
      
lineView.bgClass built.bgClass;
      
lineView.textClass built.textClass;
      
updateLineClasses(lineView);
    } else if (
cls) {
      
lineView.text.className cls;
    }
  }

  function 
updateLineClasses(lineView) {
    
updateLineBackground(lineView);
    if (
lineView.line.wrapClass)
      
ensureLineWrapped(lineView).className lineView.line.wrapClass;
    else if (
lineView.node != lineView.text)
      
lineView.node.className "";
    var 
textClass lineView.textClass lineView.textClass " " + (lineView.line.textClass || "") : lineView.line.textClass;
    
lineView.text.className textClass || "";
  }

  function 
updateLineGutter(cmlineViewlineNdims) {
    if (
lineView.gutter) {
      
lineView.node.removeChild(lineView.gutter);
      
lineView.gutter null;
    }
    var 
markers lineView.line.gutterMarkers;
    if (
cm.options.lineNumbers || markers) {
      var 
wrap ensureLineWrapped(lineView);
      var 
gutterWrap lineView.gutter =
        
wrap.insertBefore(elt("div"null"CodeMirror-gutter-wrapper""position: absolute; left: " +
                              (
cm.options.fixedGutter dims.fixedPos : -dims.gutterTotalWidth) + "px"),
                          
lineView.text);
      if (
cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
        
lineView.lineNumber gutterWrap.appendChild(
          
elt("div"lineNumberFor(cm.optionslineN),
              
"CodeMirror-linenumber CodeMirror-gutter-elt",
              
"left: " dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
              
cm.display.lineNumInnerWidth "px"));
      if (
markers) for (var 0cm.options.gutters.length; ++k) {
        var 
id cm.options.gutters[k], found markers.hasOwnProperty(id) && markers[id];
        if (
found)
          
gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt""left: " +
                                     
dims.gutterLeft[id] + "px; width: " dims.gutterWidth[id] + "px"));
      }
    }
  }

  function 
updateLineWidgets(lineViewdims) {
    if (
lineView.alignablelineView.alignable null;
    for (var 
node lineView.node.firstChildnextnodenode next) {
      var 
next node.nextSibling;
      if (
node.className == "CodeMirror-linewidget")
        
lineView.node.removeChild(node);
    }
    
insertLineWidgets(lineViewdims);
  }

  
// Build a line's DOM representation from scratch
  
function buildLineElement(cmlineViewlineNdims) {
    var 
built getLineContent(cmlineView);
    
lineView.text lineView.node built.pre;
    if (
built.bgClasslineView.bgClass built.bgClass;
    if (
built.textClasslineView.textClass built.textClass;

    
updateLineClasses(lineView);
    
updateLineGutter(cmlineViewlineNdims);
    
insertLineWidgets(lineViewdims);
    return 
lineView.node;
  }

  
// A lineView may contain multiple logical lines (when merged by
  // collapsed spans). The widgets for all of them need to be drawn.
  
function insertLineWidgets(lineViewdims) {
    
insertLineWidgetsFor(lineView.linelineViewdimstrue);
    if (
lineView.rest) for (var 0lineView.rest.lengthi++)
      
insertLineWidgetsFor(lineView.rest[i], lineViewdimsfalse);
  }

  function 
insertLineWidgetsFor(linelineViewdimsallowAbove) {
    if (!
line.widgets) return;
    var 
wrap ensureLineWrapped(lineView);
    for (var 
0ws line.widgetsws.length; ++i) {
      var 
widget ws[i], node elt("div", [widget.node], "CodeMirror-linewidget");
      if (!
widget.handleMouseEventsnode.ignoreEvents true;
      
positionLineWidget(widgetnodelineViewdims);
      if (
allowAbove && widget.above)
        
wrap.insertBefore(nodelineView.gutter || lineView.text);
      else
        
wrap.appendChild(node);
      
signalLater(widget"redraw");
    }
  }

  function 
positionLineWidget(widgetnodelineViewdims) {
    if (
widget.noHScroll) {
      (
lineView.alignable || (lineView.alignable = [])).push(node);
      var 
width dims.wrapperWidth;
      
node.style.left dims.fixedPos "px";
      if (!
widget.coverGutter) {
        
width -= dims.gutterTotalWidth;
        
node.style.paddingLeft dims.gutterTotalWidth "px";
      }
      
node.style.width width "px";
    }
    if (
widget.coverGutter) {
      
node.style.zIndex 5;
      
node.style.position "relative";
      if (!
widget.noHScrollnode.style.marginLeft = -dims.gutterTotalWidth "px";
    }
  }

  
// POSITION OBJECT

  // A Pos instance represents a position within the text.
  
var Pos CodeMirror.Pos = function(linech) {
    if (!(
this instanceof Pos)) return new Pos(linech);
    
this.line linethis.ch ch;
  };

  
// Compare two positions, return 0 if they are the same, a negative
  // number when a is less, and a positive number otherwise.
  
var cmp CodeMirror.cmpPos = function(ab) { return a.line b.line || a.ch b.ch; };

  function 
copyPos(x) {return Pos(x.linex.ch);}
  function 
maxPos(ab) { return cmp(ab) < a; }
  function 
minPos(ab) { return cmp(ab) < b; }

  
// SELECTION / CURSOR

  // Selection objects are immutable. A new one is created every time
  // the selection changes. A selection is one or more non-overlapping
  // (and non-touching) ranges, sorted, and an integer that indicates
  // which one is the primary selection (the one that's scrolled into
  // view, that getCursor returns, etc).
  
function Selection(rangesprimIndex) {
    
this.ranges ranges;
    
this.primIndex primIndex;
  }

  
Selection.prototype = {
    
primary: function() { return this.ranges[this.primIndex]; },
    
equals: function(other) {
      if (
other == this) return true;
      if (
other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
      for (var 
0this.ranges.lengthi++) {
        var 
here this.ranges[i], there other.ranges[i];
        if (
cmp(here.anchorthere.anchor) != || cmp(here.headthere.head) != 0) return false;
      }
      return 
true;
    },
    
deepCopy: function() {
      for (var 
out = [], 0this.ranges.lengthi++)
        
out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
      return new 
Selection(outthis.primIndex);
    },
    
somethingSelected: function() {
      for (var 
0this.ranges.lengthi++)
        if (!
this.ranges[i].empty()) return true;
      return 
false;
    },
    
contains: function(posend) {
      if (!
endend pos;
      for (var 
0this.ranges.lengthi++) {
        var 
range this.ranges[i];
        if (
cmp(endrange.from()) >= && cmp(posrange.to()) <= 0)
          return 
i;
      }
      return -
1;
    }
  };

  function 
Range(anchorhead) {
    
this.anchor anchorthis.head head;
  }

  
Range.prototype = {
    
from: function() { return minPos(this.anchorthis.head); },
    
to: function() { return maxPos(this.anchorthis.head); },
    empty: function() {
      return 
this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
    }
  };

  
// Take an unsorted, potentially overlapping set of ranges, and
  // build a selection out of it. 'Consumes' ranges array (modifying
  // it).
  
function normalizeSelection(rangesprimIndex) {
    var 
prim ranges[primIndex];
    
ranges.sort(function(ab) { return cmp(a.from(), b.from()); });
    
primIndex indexOf(rangesprim);
    for (var 
1ranges.lengthi++) {
      var 
cur ranges[i], prev ranges[1];
      if (
cmp(prev.to(), cur.from()) >= 0) {
        var 
from minPos(prev.from(), cur.from()), to maxPos(prev.to(), cur.to());
        var 
inv prev.empty() ? cur.from() == cur.head prev.from() == prev.head;
        if (
<= primIndex) --primIndex;
        
ranges.splice(--i2, new Range(inv to frominv from to));
      }
    }
    return new 
Selection(rangesprimIndex);
  }

  function 
simpleSelection(anchorhead) {
    return new 
Selection([new Range(anchorhead || anchor)], 0);
  }

  
// Most of the external API clips given positions to make sure they
  // actually exist within the document.
  
function clipLine(docn) {return Math.max(doc.firstMath.min(ndoc.first doc.size 1));}
  function 
clipPos(docpos) {
    if (
pos.line doc.first) return Pos(doc.first0);
    var 
last doc.first doc.size 1;
    if (
pos.line last) return Pos(lastgetLine(doclast).text.length);
    return 
clipToLen(posgetLine(docpos.line).text.length);
  }
  function 
clipToLen(poslinelen) {
    var 
ch pos.ch;
    if (
ch == null || ch linelen) return Pos(pos.linelinelen);
    else if (
ch 0) return Pos(pos.line0);
    else return 
pos;
  }
  function 
isLine(docl) {return >= doc.first && doc.first doc.size;}
  function 
clipPosArray(doc, array) {
    for (var 
out = [], 0< array.lengthi++) out[i] = clipPos(doc, array[i]);
    return 
out;
  }

  
// SELECTION UPDATES

  // The 'scroll' parameter given to many of these indicated whether
  // the new cursor position should be scrolled into view after
  // modifying the selection.

  // If shift is held or the extend flag is set, extends a range to
  // include a given position (and optionally a second position).
  // Otherwise, simply returns the range between the given positions.
  // Used for cursor motion and such.
  
function extendRange(docrangeheadother) {
    if (
doc.cm && doc.cm.display.shift || doc.extend) {
      var 
anchor range.anchor;
      if (
other) {
        var 
posBefore cmp(headanchor) < 0;
        if (
posBefore != (cmp(otheranchor) < 0)) {
          
anchor head;
          
head other;
        } else if (
posBefore != (cmp(headother) < 0)) {
          
head other;
        }
      }
      return new 
Range(anchorhead);
    } else {
      return new 
Range(other || headhead);
    }
  }

  
// Extend the primary selection range, discard the rest.
  
function extendSelection(docheadotheroptions) {
    
setSelection(doc, new Selection([extendRange(docdoc.sel.primary(), headother)], 0), options);
  }

  
// Extend all selections (pos is an array of selections with length
  // equal the number of selections)
  
function extendSelections(docheadsoptions) {
    for (var 
out = [], 0doc.sel.ranges.lengthi++)
      
out[i] = extendRange(docdoc.sel.ranges[i], heads[i], null);
    var 
newSel normalizeSelection(outdoc.sel.primIndex);
    
setSelection(docnewSeloptions);
  }

  
// Updates a single range in the selection.
  
function replaceOneSelection(docirangeoptions) {
    var 
ranges doc.sel.ranges.slice(0);
    
ranges[i] = range;
    
setSelection(docnormalizeSelection(rangesdoc.sel.primIndex), options);
  }

  
// Reset the selection to a single range.
  
function setSimpleSelection(docanchorheadoptions) {
    
setSelection(docsimpleSelection(anchorhead), options);
  }

  
// Give beforeSelectionChange handlers a change to influence a
  // selection update.
  
function filterSelectionChange(docsel) {
    var 
obj = {
      
rangessel.ranges,
      
update: function(ranges) {
        
this.ranges = [];
        for (var 
0ranges.lengthi++)
          
this.ranges[i] = new Range(clipPos(docranges[i].anchor),
                                     
clipPos(docranges[i].head));
      }
    };
    
signal(doc"beforeSelectionChange"docobj);
    if (
doc.cmsignal(doc.cm"beforeSelectionChange"doc.cmobj);
    if (
obj.ranges != sel.ranges) return normalizeSelection(obj.rangesobj.ranges.length 1);
    else return 
sel;
  }

  function 
setSelectionReplaceHistory(docseloptions) {
    var 
done doc.history.donelast lst(done);
    if (
last && last.ranges) {
      
done[done.length 1] = sel;
      
setSelectionNoUndo(docseloptions);
    } else {
      
setSelection(docseloptions);
    }
  }

  
// Set a new selection.
  
function setSelection(docseloptions) {
    
setSelectionNoUndo(docseloptions);
    
addSelectionToHistory(docdoc.seldoc.cm doc.cm.curOp.id NaNoptions);
  }

  function 
setSelectionNoUndo(docseloptions) {
    if (
hasHandler(doc"beforeSelectionChange") || doc.cm && hasHandler(doc.cm"beforeSelectionChange"))
      
sel filterSelectionChange(docsel);

    var 
bias options && options.bias ||
      (
cmp(sel.primary().headdoc.sel.primary().head) < ? -1);
    
setSelectionInner(docskipAtomicInSelection(docselbiastrue));

    if (!(
options && options.scroll === false) && doc.cm)
      
ensureCursorVisible(doc.cm);
  }

  function 
setSelectionInner(docsel) {
    if (
sel.equals(doc.sel)) return;

    
doc.sel sel;

    if (
doc.cm) {
      
doc.cm.curOp.updateInput doc.cm.curOp.selectionChanged true;
      
signalCursorActivity(doc.cm);
    }
    
signalLater(doc"cursorActivity"doc);
  }

  
// Verify that the selection does not partially select any atomic
  // marked ranges.
  
function reCheckSelection(doc) {
    
setSelectionInner(docskipAtomicInSelection(docdoc.selnullfalse), sel_dontScroll);
  }

  
// Return a selection that does not partially select any atomic
  // ranges.
  
function skipAtomicInSelection(docselbiasmayClear) {
    var 
out;
    for (var 
0sel.ranges.lengthi++) {
      var 
range sel.ranges[i];
      var 
newAnchor skipAtomic(docrange.anchorbiasmayClear);
      var 
newHead skipAtomic(docrange.headbiasmayClear);
      if (
out || newAnchor != range.anchor || newHead != range.head) {
        if (!
outout sel.ranges.slice(0i);
        
out[i] = new Range(newAnchornewHead);
      }
    }
    return 
out normalizeSelection(outsel.primIndex) : sel;
  }

  
// Ensure a given position is not inside an atomic range.
  
function skipAtomic(docposbiasmayClear) {
    var 
flipped falsecurPos pos;
    var 
dir bias || 1;
    
doc.cantEdit false;
    
search: for (;;) {
      var 
line getLine(doccurPos.line);
      if (
line.markedSpans) {
        for (var 
0line.markedSpans.length; ++i) {
          var 
sp line.markedSpans[i], sp.marker;
          if ((
sp.from == null || (m.inclusiveLeft sp.from <= curPos.ch sp.from curPos.ch)) &&
              (
sp.to == null || (m.inclusiveRight sp.to >= curPos.ch sp.to curPos.ch))) {
            if (
mayClear) {
              
signal(m"beforeCursorEnter");
              if (
m.explicitlyCleared) {
                if (!
line.markedSpans) break;
                else {--
i; continue;}
              }
            }
            if (!
m.atomic) continue;
            var 
newPos m.find(dir ? -1);
            if (
cmp(newPoscurPos) == 0) {
              
newPos.ch += dir;
              if (
newPos.ch 0) {
                if (
newPos.line doc.firstnewPos clipPos(docPos(newPos.line 1));
                else 
newPos null;
              } else if (
newPos.ch line.text.length) {
                if (
newPos.line doc.first doc.size 1newPos Pos(newPos.line 10);
                else 
newPos null;
              }
              if (!
newPos) {
                if (
flipped) {
                  
// Driven in a corner -- no valid cursor position found at all
                  // -- try again *with* clearing, if we didn't already
                  
if (!mayClear) return skipAtomic(docposbiastrue);
                  
// Otherwise, turn off editing until further notice, and return the start of the doc
                  
doc.cantEdit true;
                  return 
Pos(doc.first0);
                }
                
flipped truenewPos posdir = -dir;
              }
            }
            
curPos newPos;
            continue 
search;
          }
        }
      }
      return 
curPos;
    }
  }

  
// SELECTION DRAWING

  // Redraw the selection and/or cursor
  
function updateSelection(cm) {
    var 
display cm.displaydoc cm.doc;
    var 
curFragment document.createDocumentFragment();
    var 
selFragment document.createDocumentFragment();

    for (var 
0doc.sel.ranges.lengthi++) {
      var 
range doc.sel.ranges[i];
      var 
collapsed range.empty();
      if (
collapsed || cm.options.showCursorWhenSelecting)
        
drawSelectionCursor(cmrangecurFragment);
      if (!
collapsed)
        
drawSelectionRange(cmrangeselFragment);
    }

    
// Move the hidden textarea near the cursor to prevent scrolling artifacts
    
if (cm.options.moveInputWithCursor) {
      var 
headPos cursorCoords(cmdoc.sel.primary().head"div");
      var 
wrapOff display.wrapper.getBoundingClientRect(), lineOff display.lineDiv.getBoundingClientRect();
      var 
top Math.max(0Math.min(display.wrapper.clientHeight 10,
                                     
headPos.top lineOff.top wrapOff.top));
      var 
left Math.max(0Math.min(display.wrapper.clientWidth 10,
                                      
headPos.left lineOff.left wrapOff.left));
      
display.inputDiv.style.top top "px";
      
display.inputDiv.style.left left "px";
    }

    
removeChildrenAndAdd(display.cursorDivcurFragment);
    
removeChildrenAndAdd(display.selectionDivselFragment);
  }

  
// Draws a cursor for the given range
  
function drawSelectionCursor(cmrangeoutput) {
    var 
pos cursorCoords(cmrange.head"div");

    var 
cursor output.appendChild(elt("div""u00a0""CodeMirror-cursor"));
    
cursor.style.left pos.left "px";
    
cursor.style.top pos.top "px";
    
cursor.style.height Math.max(0pos.bottom pos.top) * cm.options.cursorHeight "px";

    if (
pos.other) {
      
// Secondary cursor, shown when on a 'jump' in bi-directional text
      
var otherCursor output.appendChild(elt("div""u00a0""CodeMirror-cursor CodeMirror-secondarycursor"));
      
otherCursor.style.display "";
      
otherCursor.style.left pos.other.left "px";
      
otherCursor.style.top pos.other.top "px";
      
otherCursor.style.height = (pos.other.bottom pos.other.top) * .85 "px";
    }
  }

  
// Draws the given range as a highlighted selection
  
function drawSelectionRange(cmrangeoutput) {
    var 
display cm.displaydoc cm.doc;
    var 
fragment document.createDocumentFragment();
    var 
padding paddingH(cm.display), leftSide padding.leftrightSide display.lineSpace.offsetWidth padding.right;

    function 
add(lefttopwidthbottom) {
      if (
top 0top 0;
      
top Math.round(top);
      
bottom Math.round(bottom);
      
fragment.appendChild(elt("div"null"CodeMirror-selected""position: absolute; left: " left +
                               
"px; top: " top "px; width: " + (width == null rightSide left width) +
                               
"px; height: " + (bottom top) + "px"));
    }

    function 
drawForLine(linefromArgtoArg) {
      var 
lineObj getLine(docline);
      var 
lineLen lineObj.text.length;
      var 
startend;
      function 
coords(chbias) {
        return 
charCoords(cmPos(linech), "div"lineObjbias);
      }

      
iterateBidiSections(getOrder(lineObj), fromArg || 0toArg == null lineLen toArg, function(fromtodir) {
        var 
leftPos coords(from"left"), rightPosleftright;
        if (
from == to) {
          
rightPos leftPos;
          
left right leftPos.left;
        } else {
          
rightPos coords(to 1"right");
          if (
dir == "rtl") { var tmp leftPosleftPos rightPosrightPos tmp; }
          
left leftPos.left;
          
right rightPos.right;
        }
        if (
fromArg == null && from == 0left leftSide;
        if (
rightPos.top leftPos.top 3) { // Different lines, draw top part
          
add(leftleftPos.topnullleftPos.bottom);
          
left leftSide;
          if (
leftPos.bottom rightPos.topadd(leftleftPos.bottomnullrightPos.top);
        }
        if (
toArg == null && to == lineLenright rightSide;
        if (!
start || leftPos.top start.top || leftPos.top == start.top && leftPos.left start.left)
          
start leftPos;
        if (!
end || rightPos.bottom end.bottom || rightPos.bottom == end.bottom && rightPos.right end.right)
          
end rightPos;
        if (
left leftSide 1left leftSide;
        
add(leftrightPos.topright leftrightPos.bottom);
      });
      return {
startstartendend};
    }

    var 
sFrom range.from(), sTo range.to();
    if (
sFrom.line == sTo.line) {
      
drawForLine(sFrom.linesFrom.chsTo.ch);
    } else {
      var 
fromLine getLine(docsFrom.line), toLine getLine(docsTo.line);
      var 
singleVLine visualLine(fromLine) == visualLine(toLine);
      var 
leftEnd drawForLine(sFrom.linesFrom.chsingleVLine fromLine.text.length null).end;
      var 
rightStart drawForLine(sTo.linesingleVLine nullsTo.ch).start;
      if (
singleVLine) {
        if (
leftEnd.top rightStart.top 2) {
          
add(leftEnd.rightleftEnd.topnullleftEnd.bottom);
          
add(leftSiderightStart.toprightStart.leftrightStart.bottom);
        } else {
          
add(leftEnd.rightleftEnd.toprightStart.left leftEnd.rightleftEnd.bottom);
        }
      }
      if (
leftEnd.bottom rightStart.top)
        
add(leftSideleftEnd.bottomnullrightStart.top);
    }

    
output.appendChild(fragment);
  }

  
// Cursor-blinking
  
function restartBlink(cm) {
    if (!
cm.state.focused) return;
    var 
display cm.display;
    
clearInterval(display.blinker);
    var 
on true;
    
display.cursorDiv.style.visibility "";
    if (
cm.options.cursorBlinkRate 0)
      
display.blinker setInterval(function() {
        
display.cursorDiv.style.visibility = (on = !on) ? "" "hidden";
      }, 
cm.options.cursorBlinkRate);
  }

  
// HIGHLIGHT WORKER

  
function startWorker(cmtime) {
    if (
cm.doc.mode.startState && cm.doc.frontier cm.display.viewTo)
      
cm.state.highlight.set(timebind(highlightWorkercm));
  }

  function 
highlightWorker(cm) {
    var 
doc cm.doc;
    if (
doc.frontier doc.firstdoc.frontier doc.first;
    if (
doc.frontier >= cm.display.viewTo) return;
    var 
end = +new Date cm.options.workTime;
    var 
state copyState(doc.modegetStateBefore(cmdoc.frontier));

    
runInOp(cm, function() {
    
doc.iter(doc.frontierMath.min(doc.first doc.sizecm.display.viewTo 500), function(line) {
      if (
doc.frontier >= cm.display.viewFrom) { // Visible
        
var oldStyles line.styles;
        var 
highlighted highlightLine(cmlinestatetrue);
        
line.styles highlighted.styles;
        if (
highlighted.classesline.styleClasses highlighted.classes;
        else if (
line.styleClassesline.styleClasses null;
        var 
ischange = !oldStyles || oldStyles.length != line.styles.length;
        for (var 
0; !ischange && oldStyles.length; ++iischange oldStyles[i] != line.styles[i];
        if (
ischangeregLineChange(cmdoc.frontier"text");
        
line.stateAfter copyState(doc.modestate);
      } else {
        
processLine(cmline.textstate);
        
line.stateAfter doc.frontier == copyState(doc.modestate) : null;
      }
      ++
doc.frontier;
      if (+new 
Date end) {
        
startWorker(cmcm.options.workDelay);
        return 
true;
      }
    });
    });
  }

  
// Finds the line to start with when starting a parse. Tries to
  // find a line with a stateAfter, so that it can start with a
  // valid state. If that fails, it returns the line with the
  // smallest indentation, which tends to need the least context to
  // parse correctly.
  
function findStartLine(cmnprecise) {
    var 
minindentminlinedoc cm.doc;
    var 
lim precise ? -- (cm.doc.mode.innerMode 1000 100);
    for (var 
search nsearch lim; --search) {
      if (
search <= doc.first) return doc.first;
      var 
line getLine(docsearch 1);
      if (
line.stateAfter && (!precise || search <= doc.frontier)) return search;
      var 
indented countColumn(line.textnullcm.options.tabSize);
      if (
minline == null || minindent indented) {
        
minline search 1;
        
minindent indented;
      }
    }
    return 
minline;
  }

  function 
getStateBefore(cmnprecise) {
    var 
doc cm.docdisplay cm.display;
    if (!
doc.mode.startState) return true;
    var 
pos findStartLine(cmnprecise), state pos doc.first && getLine(docpos-1).stateAfter;
    if (!
statestate startState(doc.mode);
    else 
state copyState(doc.modestate);
    
doc.iter(posn, function(line) {
      
processLine(cmline.textstate);
      var 
save pos == || pos == || pos >= display.viewFrom && pos display.viewTo;
      
line.stateAfter save copyState(doc.modestate) : null;
      ++
pos;
    });
    if (
precisedoc.frontier pos;
    return 
state;
  }

  
// POSITION MEASUREMENT

  
function paddingTop(display) {return display.lineSpace.offsetTop;}
  function 
paddingVert(display) {return display.mover.offsetHeight display.lineSpace.offsetHeight;}
  function 
paddingH(display) {
    if (
display.cachedPaddingH) return display.cachedPaddingH;
    var 
removeChildrenAndAdd(display.measureelt("pre""x"));
    var 
style window.getComputedStyle window.getComputedStyle(e) : e.currentStyle;
    var 
data = {leftparseInt(style.paddingLeft), rightparseInt(style.paddingRight)};
    if (!
isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH data;
    return 
data;
  }

  
// Ensure the lineView.wrapping.heights array is populated. This is
  // an array of bottom offsets for the lines that make up a drawn
  // line. When lineWrapping is on, there might be more than one
  // height.
  
function ensureLineHeights(cmlineViewrect) {
    var 
wrapping cm.options.lineWrapping;
    var 
curWidth wrapping && cm.display.scroller.clientWidth;
    if (!
lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
      var 
heights lineView.measure.heights = [];
      if (
wrapping) {
        
lineView.measure.width curWidth;
        var 
rects lineView.text.firstChild.getClientRects();
        for (var 
0rects.length 1i++) {
          var 
cur rects[i], next rects[1];
          if (
Math.abs(cur.bottom next.bottom) > 2)
            
heights.push((cur.bottom next.top) / rect.top);
        }
      }
      
heights.push(rect.bottom rect.top);
    }
  }

  
// Find a line map (mapping character offsets to text nodes) and a
  // measurement cache for the given line number. (A line view might
  // contain multiple lines when collapsed ranges are present.)
  
function mapFromLineView(lineViewlinelineN) {
    if (
lineView.line == line)
      return {
maplineView.measure.mapcachelineView.measure.cache};
    for (var 
0lineView.rest.lengthi++)
      if (
lineView.rest[i] == line)
        return {
maplineView.measure.maps[i], cachelineView.measure.caches[i]};
    for (var 
0lineView.rest.lengthi++)
      if (
lineNo(lineView.rest[i]) > lineN)
        return {
maplineView.measure.maps[i], cachelineView.measure.caches[i], beforetrue};
  }

  
// Render a line into the hidden node display.externalMeasured. Used
  // when measurement is needed for a line that's not in the viewport.
  
function updateExternalMeasurement(cmline) {
    
line visualLine(line);
    var 
lineN lineNo(line);
    var 
view cm.display.externalMeasured = new LineView(cm.doclinelineN);
    
view.lineN lineN;
    var 
built view.built buildLineContent(cmview);
    
view.text built.pre;
    
removeChildrenAndAdd(cm.display.lineMeasurebuilt.pre);
    return 
view;
  }

  
// Get a {top, bottom, left, right} box (in line-local coordinates)
  // for a given character.
  
function measureChar(cmlinechbias) {
    return 
measureCharPrepared(cmprepareMeasureForLine(cmline), chbias);
  }

  
// Find a line view that corresponds to the given line number.
  
function findViewForLine(cmlineN) {
    if (
lineN >= cm.display.viewFrom && lineN cm.display.viewTo)
      return 
cm.display.view[findViewIndex(cmlineN)];
    var 
ext cm.display.externalMeasured;
    if (
ext && lineN >= ext.lineN && lineN ext.lineN ext.size)
      return 
ext;
  }

  
// Measurement can be split in two steps, the set-up work that
  // applies to the whole line, and the measurement of the actual
  // character. Functions like coordsChar, that need to do a lot of
  // measurements in a row, can thus ensure that the set-up work is
  // only done once.
  
function prepareMeasureForLine(cmline) {
    var 
lineN lineNo(line);
    var 
view findViewForLine(cmlineN);
    if (
view && !view.text)
      
view null;
    else if (
view && view.changes)
      
updateLineForChanges(cmviewlineNgetDimensions(cm));
    if (!
view)
      
view updateExternalMeasurement(cmline);

    var 
info mapFromLineView(viewlinelineN);
    return {
      
linelineviewviewrectnull,
      
mapinfo.mapcacheinfo.cachebeforeinfo.before,
      
hasHeightsfalse
    
};
  }

  
// Given a prepared measurement object, measures the position of an
  // actual character (or fetches it from the cache).
  
function measureCharPrepared(cmpreparedchbias) {
    if (
prepared.beforech = -1;
    var 
key ch + (bias || ""), found;
    if (
prepared.cache.hasOwnProperty(key)) {
      
found prepared.cache[key];
    } else {
      if (!
prepared.rect)
        
prepared.rect prepared.view.text.getBoundingClientRect();
      if (!
prepared.hasHeights) {
        
ensureLineHeights(cmprepared.viewprepared.rect);
        
prepared.hasHeights true;
      }
      
found measureCharInner(cmpreparedchbias);
      if (!
found.bogusprepared.cache[key] = found;
    }
    return {
leftfound.leftrightfound.righttopfound.topbottomfound.bottom};
  }

  var 
nullRect = {left0right0top0bottom0};

  function 
measureCharInner(cmpreparedchbias) {
    var 
map prepared.map;

    var 
nodestartendcollapse;
    
// First, search the line map for the text node corresponding to,
    // or closest to, the target character.
    
for (var 0map.length+= 3) {
      var 
mStart map[i], mEnd map[1];
      if (
ch mStart) {
        
start 0end 1;
        
collapse "left";
      } else if (
ch mEnd) {
        
start ch mStart;
        
end start 1;
      } else if (
== map.length || ch == mEnd && map[3] > ch) {
        
end mEnd mStart;
        
start end 1;
        if (
ch >= mEndcollapse "right";
      }
      if (
start != null) {
        
node map[2];
        if (
mStart == mEnd && bias == (node.insertLeft "left" "right"))
          
collapse bias;
        if (
bias == "left" && start == 0)
          while (
&& map[2] == map[3] && map[1].insertLeft) {
            
node map[(-= 3) + 2];
            
collapse "left";
          }
        if (
bias == "right" && start == mEnd mStart)
          while (
map.length && map[3] == map[4] && !map[5].insertLeft) {
            
node map[(+= 3) + 2];
            
collapse "right";
          }
        break;
      }
    }

    var 
rect;
    if (
node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
      
while (start && isExtendingChar(prepared.line.text.charAt(mStart start))) --start;
      while (
mStart end mEnd && isExtendingChar(prepared.line.text.charAt(mStart end))) ++end;
      if (
ie_upto8 && start == && end == mEnd mStart) {
        
rect node.parentNode.getBoundingClientRect();
      } else if (
ie && cm.options.lineWrapping) {
        var 
rects range(nodestartend).getClientRects();
        if (
rects.length)
          
rect rects[bias == "right" rects.length 0];
        else
          
rect nullRect;
      } else {
        
rect range(nodestartend).getBoundingClientRect() || nullRect;
      }
    } else { 
// If it is a widget, simply get the box for the whole widget.
      
if (start 0collapse bias "right";
      var 
rects;
      if (
cm.options.lineWrapping && (rects node.getClientRects()).length 1)
        
rect rects[bias == "right" rects.length 0];
      else
        
rect node.getBoundingClientRect();
    }
    if (
ie_upto8 && !start && (!rect || !rect.left && !rect.right)) {
      var 
rSpan node.parentNode.getClientRects()[0];
      if (
rSpan)
        
rect = {leftrSpan.leftrightrSpan.left charWidth(cm.display), toprSpan.topbottomrSpan.bottom};
      else
        
rect nullRect;
    }

    var 
topbot = (rect.bottom rect.top) / prepared.rect.top;
    var 
heights prepared.view.measure.heights;
    for (var 
0heights.length 1i++)
      if (
bot heights[i]) break;
    
top heights[1] : 0bot heights[i];
    var 
result = {left: (collapse == "right" rect.right rect.left) - prepared.rect.left,
                  
right: (collapse == "left" rect.left rect.right) - prepared.rect.left,
                  
toptopbottombot};
    if (!
rect.left && !rect.rightresult.bogus true;
    return 
result;
  }

  function 
clearLineMeasurementCacheFor(lineView) {
    if (
lineView.measure) {
      
lineView.measure.cache = {};
      
lineView.measure.heights null;
      if (
lineView.rest) for (var 0lineView.rest.lengthi++)
        
lineView.measure.caches[i] = {};
    }
  }

  function 
clearLineMeasurementCache(cm) {
    
cm.display.externalMeasure null;
    
removeChildren(cm.display.lineMeasure);
    for (var 
0cm.display.view.lengthi++)
      
clearLineMeasurementCacheFor(cm.display.view[i]);
  }

  function 
clearCaches(cm) {
    
clearLineMeasurementCache(cm);
    
cm.display.cachedCharWidth cm.display.cachedTextHeight cm.display.cachedPaddingH null;
    if (!
cm.options.lineWrappingcm.display.maxLineChanged true;
    
cm.display.lineNumChars null;
  }

  function 
pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
  function 
pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }

  
// Converts a {top, bottom, left, right} box from line-local
  // coordinates into another coordinate system. Context may be one of
  // "line", "div" (display.lineDiv), "local"/null (editor), or "page".
  
function intoCoordSystem(cmlineObjrectcontext) {
    if (
lineObj.widgets) for (var 0lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
      var 
size widgetHeight(lineObj.widgets[i]);
      
rect.top += sizerect.bottom += size;
    }
    if (
context == "line") return rect;
    if (!
contextcontext "local";
    var 
yOff heightAtLine(lineObj);
    if (
context == "local"yOff += paddingTop(cm.display);
    else 
yOff -= cm.display.viewOffset;
    if (
context == "page" || context == "window") {
      var 
lOff cm.display.lineSpace.getBoundingClientRect();
      
yOff += lOff.top + (context == "window" pageScrollY());
      var 
xOff lOff.left + (context == "window" pageScrollX());
      
rect.left += xOffrect.right += xOff;
    }
    
rect.top += yOffrect.bottom += yOff;
    return 
rect;
  }

  
// Coverts a box from "div" coords to another coordinate system.
  // Context may be "window", "page", "div", or "local"/null.
  
function fromCoordSystem(cmcoordscontext) {
    if (
context == "div") return coords;
    var 
left coords.lefttop coords.top;
    
// First move into "page" coordinate system
    
if (context == "page") {
      
left -= pageScrollX();
      
top -= pageScrollY();
    } else if (
context == "local" || !context) {
      var 
localBox cm.display.sizer.getBoundingClientRect();
      
left += localBox.left;
      
top += localBox.top;
    }

    var 
lineSpaceBox cm.display.lineSpace.getBoundingClientRect();
    return {
leftleft lineSpaceBox.lefttoptop lineSpaceBox.top};
  }

  function 
charCoords(cmposcontextlineObjbias) {
    if (!
lineObjlineObj getLine(cm.docpos.line);
    return 
intoCoordSystem(cmlineObjmeasureChar(cmlineObjpos.chbias), context);
  }

  
// Returns a box for a given cursor position, which may have an
  // 'other' property containing the position of the secondary cursor
  // on a bidi boundary.
  
function cursorCoords(cmposcontextlineObjpreparedMeasure) {
    
lineObj lineObj || getLine(cm.docpos.line);
    if (!
preparedMeasurepreparedMeasure prepareMeasureForLine(cmlineObj);
    function 
get(chright) {
      var 
measureCharPrepared(cmpreparedMeasurechright "right" "left");
      if (
rightm.left m.right; else m.right m.left;
      return 
intoCoordSystem(cmlineObjmcontext);
    }
    function 
getBidi(chpartPos) {
      var 
part order[partPos], right part.level 2;
      if (
ch == bidiLeft(part) && partPos && part.level order[partPos 1].level) {
        
part order[--partPos];
        
ch bidiRight(part) - (part.level 1);
        
right true;
      } else if (
ch == bidiRight(part) && partPos order.length && part.level order[partPos 1].level) {
        
part order[++partPos];
        
ch bidiLeft(part) - part.level 2;
        
right false;
      }
      if (
right && ch == part.to && ch part.from) return get(ch 1);
      return 
get(chright);
    }
    var 
order getOrder(lineObj), ch pos.ch;
    if (!
order) return get(ch);
    var 
partPos getBidiPartAt(orderch);
    var 
val getBidi(chpartPos);
    if (
bidiOther != nullval.other getBidi(chbidiOther);
    return 
val;
  }

  
// Used to cheaply estimate the coordinates for a position. Used for
  // intermediate scroll updates.
  
function estimateCoords(cmpos) {
    var 
left 0pos clipPos(cm.docpos);
    if (!
cm.options.lineWrappingleft charWidth(cm.display) * pos.ch;
    var 
lineObj getLine(cm.docpos.line);
    var 
top heightAtLine(lineObj) + paddingTop(cm.display);
    return {
leftleftrightlefttoptopbottomtop lineObj.height};
  }

  
// Positions returned by coordsChar contain some extra information.
  // xRel is the relative x position of the input coordinates compared
  // to the found position (so xRel > 0 means the coordinates are to
  // the right of the character position, for example). When outside
  // is true, that means the coordinates lie outside the line's
  // vertical range.
  
function PosWithInfo(linechoutsidexRel) {
    var 
pos Pos(linech);
    
pos.xRel xRel;
    if (
outsidepos.outside true;
    return 
pos;
  }

  
// Compute the character position closest to the given coordinates.
  // Input must be lineSpace-local ("div" coordinate system).
  
function coordsChar(cmxy) {
    var 
doc cm.doc;
    
+= cm.display.viewOffset;
    if (
0) return PosWithInfo(doc.first0true, -1);
    var 
lineN lineAtHeight(docy), last doc.first doc.size 1;
    if (
lineN last)
      return 
PosWithInfo(doc.first doc.size 1getLine(doclast).text.lengthtrue1);
    if (
00;

    var 
lineObj getLine(doclineN);
    for (;;) {
      var 
found coordsCharInner(cmlineObjlineNxy);
      var 
merged collapsedSpanAtEnd(lineObj);
      var 
mergedPos merged && merged.find(0true);
      if (
merged && (found.ch mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel 0))
        
lineN lineNo(lineObj mergedPos.to.line);
      else
        return 
found;
    }
  }

  function 
coordsCharInner(cmlineObjlineNoxy) {
    var 
innerOff heightAtLine(lineObj);
    var 
wrongLine falseadjust cm.display.wrapper.clientWidth;
    var 
preparedMeasure prepareMeasureForLine(cmlineObj);

    function 
getX(ch) {
      var 
sp cursorCoords(cmPos(lineNoch), "line"lineObjpreparedMeasure);
      
wrongLine true;
      if (
innerOff sp.bottom) return sp.left adjust;
      else if (
innerOff sp.top) return sp.left adjust;
      else 
wrongLine false;
      return 
sp.left;
    }

    var 
bidi getOrder(lineObj), dist lineObj.text.length;
    var 
from lineLeft(lineObj), to lineRight(lineObj);
    var 
fromX getX(from), fromOutside wrongLinetoX getX(to), toOutside wrongLine;

    if (
toX) return PosWithInfo(lineNototoOutside1);
    
// Do a binary search between these bounds.
    
for (;;) {
      if (
bidi to == from || to == moveVisually(lineObjfrom1) : to from <= 1) {
        var 
ch fromX || fromX <= toX from to;
        var 
xDiff - (ch == from fromX toX);
        while (
isExtendingChar(lineObj.text.charAt(ch))) ++ch;
        var 
pos PosWithInfo(lineNochch == from fromOutside toOutside,
                              
xDiff < -? -xDiff 0);
        return 
pos;
      }
      var 
step Math.ceil(dist 2), middle from step;
      if (
bidi) {
        
middle from;
        for (var 
0step; ++imiddle moveVisually(lineObjmiddle1);
      }
      var 
middleX getX(middle);
      if (
middleX x) {to middletoX middleX; if (toOutside wrongLinetoX += 1000dist step;}
      else {
from middlefromX middleXfromOutside wrongLinedist -= step;}
    }
  }

  var 
measureText;
  
// Compute the default text height.
  
function textHeight(display) {
    if (
display.cachedTextHeight != null) return display.cachedTextHeight;
    if (
measureText == null) {
      
measureText elt("pre");
      
// Measure a bunch of lines, for browsers that compute
      // fractional heights.
      
for (var 049; ++i) {
        
measureText.appendChild(document.createTextNode("x"));
        
measureText.appendChild(elt("br"));
      }
      
measureText.appendChild(document.createTextNode("x"));
    }
    
removeChildrenAndAdd(display.measuremeasureText);
    var 
height measureText.offsetHeight 50;
    if (
height 3display.cachedTextHeight height;
    
removeChildren(display.measure);
    return 
height || 1;
  }

  
// Compute the default character width.
  
function charWidth(display) {
    if (
display.cachedCharWidth != null) return display.cachedCharWidth;
    var 
anchor elt("span""xxxxxxxxxx");
    var 
pre elt("pre", [anchor]);
    
removeChildrenAndAdd(display.measurepre);
    var 
rect anchor.getBoundingClientRect(), width = (rect.right rect.left) / 10;
    if (
width 2display.cachedCharWidth width;
    return 
width || 10;
  }

  
// OPERATIONS

  // Operations are used to wrap a series of changes to the editor
  // state in such a way that each change won't have to update the
  // cursor and display (which would be awkward, slow, and
  // error-prone). Instead, display updates are batched and then all
  // combined and executed at once.

  
var nextOpId 0;
  
// Start a new operation.
  
function startOperation(cm) {
    
cm.curOp = {
      
viewChangedfalse,      // Flag that indicates that lines might need to be redrawn
      
startHeightcm.doc.height// Used to detect need to update scrollbar
      
forceUpdatefalse,      // Used to force a redraw
      
updateInputnull,       // Whether to reset the input textarea
      
typingfalse,           // Whether this reset should be careful to leave existing text (for compositing)
      
changeObjsnull,        // Accumulated changes, for firing change events
      
cursorActivityHandlersnull// Set of handlers to fire cursorActivity on
      
selectionChangedfalse// Whether the selection needs to be redrawn
      
updateMaxLinefalse,    // Set when the widest line needs to be determined anew
      
scrollLeftnullscrollTopnull// Intermediate scroll position, not pushed to DOM yet
      
scrollToPosnull,       // Used to scroll to a specific position
      
id: ++nextOpId           // Unique ID
    
};
    if (!
delayedCallbackDepth++) delayedCallbacks = [];
  }

  
// Finish an operation, updating the display and signalling delayed events
  
function endOperation(cm) {
    var 
op cm.curOpdoc cm.docdisplay cm.display;
    
cm.curOp null;

    if (
op.updateMaxLinefindMaxLine(cm);

    
// If it looks like an update might be needed, call updateDisplay
    
if (op.viewChanged || op.forceUpdate || op.scrollTop != null ||
        
op.scrollToPos && (op.scrollToPos.from.line display.viewFrom ||
                           
op.scrollToPos.to.line >= display.viewTo) ||
        
display.maxLineChanged && cm.options.lineWrapping) {
      var 
updated updateDisplay(cm, {topop.scrollTopensureop.scrollToPos}, op.forceUpdate);
      if (
cm.display.scroller.offsetHeightcm.doc.scrollTop cm.display.scroller.scrollTop;
    }
    
// If no update was run, but the selection changed, redraw that.
    
if (!updated && op.selectionChangedupdateSelection(cm);
    if (!
updated && op.startHeight != cm.doc.heightupdateScrollbars(cm);

    
// Abort mouse wheel delta measurement, when scrolling explicitly
    
if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
      
display.wheelStartX display.wheelStartY null;

    
// Propagate the scroll position to the actual DOM scroller
    
if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) {
      var 
top Math.max(0Math.min(display.scroller.scrollHeight display.scroller.clientHeightop.scrollTop));
      
display.scroller.scrollTop display.scrollbarV.scrollTop doc.scrollTop top;
    }
    if (
op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) {
      var 
left Math.max(0Math.min(display.scroller.scrollWidth display.scroller.clientWidthop.scrollLeft));
      
display.scroller.scrollLeft display.scrollbarH.scrollLeft doc.scrollLeft left;
      
alignHorizontally(cm);
    }
    
// If we need to scroll a specific position into view, do so.
    
if (op.scrollToPos) {
      var 
coords scrollPosIntoView(cmclipPos(cm.docop.scrollToPos.from),
                                     
clipPos(cm.docop.scrollToPos.to), op.scrollToPos.margin);
      if (
op.scrollToPos.isCursor && cm.state.focusedmaybeScrollWindow(cmcoords);
    }

    if (
op.selectionChangedrestartBlink(cm);

    if (
cm.state.focused && op.updateInput)
      
resetInput(cmop.typing);

    
// Fire events for markers that are hidden/unidden by editing or
    // undoing
    
var hidden op.maybeHiddenMarkersunhidden op.maybeUnhiddenMarkers;
    if (
hidden) for (var 0hidden.length; ++i)
      if (!
hidden[i].lines.lengthsignal(hidden[i], "hide");
    if (
unhidden) for (var 0unhidden.length; ++i)
      if (
unhidden[i].lines.lengthsignal(unhidden[i], "unhide");

    var 
delayed;
    if (!--
delayedCallbackDepth) {
      
delayed delayedCallbacks;
      
delayedCallbacks null;
    }
    
// Fire change events, and delayed event handlers
    
if (op.changeObjs)
      
signal(cm"changes"cmop.changeObjs);
    if (
delayed) for (var 0delayed.length; ++idelayed[i]();
    if (
op.cursorActivityHandlers)
      for (var 
0op.cursorActivityHandlers.lengthi++)
        
op.cursorActivityHandlers[i](cm);
  }

  
// Run the given function in an operation
  
function runInOp(cmf) {
    if (
cm.curOp) return f();
    
startOperation(cm);
    try { return 
f(); }
    finally { 
endOperation(cm); }
  }
  
// Wraps a function in an operation. Returns the wrapped function.
  
function operation(cmf) {
    return function() {
      if (
cm.curOp) return f.apply(cmarguments);
      
startOperation(cm);
      try { return 
f.apply(cmarguments); }
      finally { 
endOperation(cm); }
    };
  }
  
// Used to add methods to editor and doc instances, wrapping them in
  // operations.
  
function methodOp(f) {
    return function() {
      if (
this.curOp) return f.apply(thisarguments);
      
startOperation(this);
      try { return 
f.apply(thisarguments); }
      finally { 
endOperation(this); }
    };
  }
  function 
docMethodOp(f) {
    return function() {
      var 
cm this.cm;
      if (!
cm || cm.curOp) return f.apply(thisarguments);
      
startOperation(cm);
      try { return 
f.apply(thisarguments); }
      finally { 
endOperation(cm); }
    };
  }

  
// VIEW TRACKING

  // These objects are used to represent the visible (currently drawn)
  // part of the document. A LineView may correspond to multiple
  // logical lines, if those are connected by collapsed ranges.
  
function LineView(doclinelineN) {
    
// The starting line
    
this.line line;
    
// Continuing lines, if any
    
this.rest visualLineContinued(line);
    
// Number of logical lines in this visual line
    
this.size this.rest lineNo(lst(this.rest)) - lineN 1;
    
this.node this.text null;
    
this.hidden lineIsHidden(docline);
  }

  
// Create a range of LineView objects for the given lines.
  
function buildViewArray(cmfromto) {
    var array = [], 
nextPos;
    for (var 
pos frompos topos nextPos) {
      var 
view = new LineView(cm.docgetLine(cm.docpos), pos);
      
nextPos pos view.size;
      array.
push(view);
    }
    return array;
  }

  
// Updates the display.view data structure for a given change to the
  // document. From and to are in pre-change coordinates. Lendiff is
  // the amount of lines added or subtracted by the change. This is
  // used for changes that span multiple lines, or change the way
  // lines are divided into visual lines. regLineChange (below)
  // registers single-line changes.
  
function regChange(cmfromtolendiff) {
    if (
from == nullfrom cm.doc.first;
    if (
to == nullto cm.doc.first cm.doc.size;
    if (!
lendifflendiff 0;

    var 
display cm.display;
    if (
lendiff && to display.viewTo &&
        (
display.updateLineNumbers == null || display.updateLineNumbers from))
      
display.updateLineNumbers from;

    
cm.curOp.viewChanged true;

    if (
from >= display.viewTo) { // Change after
      
if (sawCollapsedSpans && visualLineNo(cm.docfrom) < display.viewTo)
        
resetView(cm);
    } else if (
to <= display.viewFrom) { // Change before
      
if (sawCollapsedSpans && visualLineEndNo(cm.docto lendiff) > display.viewFrom) {
        
resetView(cm);
      } else {
        
display.viewFrom += lendiff;
        
display.viewTo += lendiff;
      }
    } else if (
from <= display.viewFrom && to >= display.viewTo) { // Full overlap
      
resetView(cm);
    } else if (
from <= display.viewFrom) { // Top overlap
      
var cut viewCuttingPoint(cmtoto lendiff1);
      if (
cut) {
        
display.view display.view.slice(cut.index);
        
display.viewFrom cut.lineN;
        
display.viewTo += lendiff;
      } else {
        
resetView(cm);
      }
    } else if (
to >= display.viewTo) { // Bottom overlap
      
var cut viewCuttingPoint(cmfromfrom, -1);
      if (
cut) {
        
display.view display.view.slice(0cut.index);
        
display.viewTo cut.lineN;
      } else {
        
resetView(cm);
      }
    } else { 
// Gap in the middle
      
var cutTop viewCuttingPoint(cmfromfrom, -1);
      var 
cutBot viewCuttingPoint(cmtoto lendiff1);
      if (
cutTop && cutBot) {
        
display.view display.view.slice(0cutTop.index)
          .
concat(buildViewArray(cmcutTop.lineNcutBot.lineN))
          .
concat(display.view.slice(cutBot.index));
        
display.viewTo += lendiff;
      } else {
        
resetView(cm);
      }
    }

    var 
ext display.externalMeasured;
    if (
ext) {
      if (
to ext.lineN)
        
ext.lineN += lendiff;
      else if (
from ext.lineN ext.size)
        
display.externalMeasured null;
    }
  }

  
// Register a change to a single line. Type must be one of "text",
  // "gutter", "class", "widget"
  
function regLineChange(cmlinetype) {
    
cm.curOp.viewChanged true;
    var 
display cm.displayext cm.display.externalMeasured;
    if (
ext && line >= ext.lineN && line ext.lineN ext.size)
      
display.externalMeasured null;

    if (
line display.viewFrom || line >= display.viewTo) return;
    var 
lineView display.view[findViewIndex(cmline)];
    if (
lineView.node == null) return;
    var 
arr lineView.changes || (lineView.changes = []);
    if (
indexOf(arrtype) == -1arr.push(type);
  }

  
// Clear the view.
  
function resetView(cm) {
    
cm.display.viewFrom cm.display.viewTo cm.doc.first;
    
cm.display.view = [];
    
cm.display.viewOffset 0;
  }

  
// Find the view element corresponding to a given line. Return null
  // when the line isn't visible.
  
function findViewIndex(cmn) {
    if (
>= cm.display.viewTo) return null;
    
-= cm.display.viewFrom;
    if (
0) return null;
    var 
view cm.display.view;
    for (var 
0view.lengthi++) {
      
-= view[i].size;
      if (
0) return i;
    }
  }

  function 
viewCuttingPoint(cmoldNnewNdir) {
    var 
index findViewIndex(cmoldN), diffview cm.display.view;
    if (!
sawCollapsedSpans || newN == cm.doc.first cm.doc.size)
      return {
indexindexlineNnewN};
    for (var 
0cm.display.viewFromindexi++)
      
+= view[i].size;
    if (
!= oldN) {
      if (
dir 0) {
        if (
index == view.length 1) return null;
        
diff = (view[index].size) - oldN;
        
index++;
      } else {
        
diff oldN;
      }
      
oldN += diffnewN += diff;
    }
    while (
visualLineNo(cm.docnewN) != newN) {
      if (
index == (dir view.length 1)) return null;
      
newN += dir view[index - (dir 0)].size;
      
index += dir;
    }
    return {
indexindexlineNnewN};
  }

  
// Force the view to cover a given range, adding empty view element
  // or clipping off existing ones as needed.
  
function adjustView(cmfromto) {
    var 
display cm.displayview display.view;
    if (
view.length == || from >= display.viewTo || to <= display.viewFrom) {
      
display.view buildViewArray(cmfromto);
      
display.viewFrom from;
    } else {
      if (
display.viewFrom from)
        
display.view buildViewArray(cmfromdisplay.viewFrom).concat(display.view);
      else if (
display.viewFrom from)
        
display.view display.view.slice(findViewIndex(cmfrom));
      
display.viewFrom from;
      if (
display.viewTo to)
        
display.view display.view.concat(buildViewArray(cmdisplay.viewToto));
      else if (
display.viewTo to)
        
display.view display.view.slice(0findViewIndex(cmto));
    }
    
display.viewTo to;
  }

  
// Count the number of lines in the view whose DOM representation is
  // out of date (or nonexistent).
  
function countDirtyView(cm) {
    var 
view cm.display.viewdirty 0;
    for (var 
0view.lengthi++) {
      var 
lineView view[i];
      if (!
lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
    }
    return 
dirty;
  }

  
// INPUT HANDLING

  // Poll for input changes, using the normal rate of polling. This
  // runs as long as the editor is focused.
  
function slowPoll(cm) {
    if (
cm.display.pollingFast) return;
    
cm.display.poll.set(cm.options.pollInterval, function() {
      
readInput(cm);
      if (
cm.state.focusedslowPoll(cm);
    });
  }

  
// When an event has just come in that is likely to add or change
  // something in the input textarea, we poll faster, to ensure that
  // the change appears on the screen quickly.
  
function fastPoll(cm) {
    var 
missed false;
    
cm.display.pollingFast true;
    function 
p() {
      var 
changed readInput(cm);
      if (!
changed && !missed) {missed truecm.display.poll.set(60p);}
      else {
cm.display.pollingFast falseslowPoll(cm);}
    }
    
cm.display.poll.set(20p);
  }

  
// Read input from the textarea, and update the document to match.
  // When something is selected, it is present in the textarea, and
  // selected (unless it is huge, in which case a placeholder is
  // used). When nothing is selected, the cursor sits after previously
  // seen text (can be empty), which is stored in prevInput (we must
  // not reset the textarea when typing, because that breaks IME).
  
function readInput(cm) {
    var 
input cm.display.inputprevInput cm.display.prevInputdoc cm.doc;
    
// Since this is called a *lot*, try to bail out as cheaply as
    // possible when it is clear that nothing happened. hasSelection
    // will be the case when there is a lot of text in the textarea,
    // in which case reading its value would be expensive.
    
if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput)
      return 
false;
    
// See paste handler for more on the fakedLastChar kludge
    
if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
      
input.value input.value.substring(0input.value.length 1);
      
cm.state.fakedLastChar false;
    }
    var 
text input.value;
    
// If nothing changed, bail.
    
if (text == prevInput && !cm.somethingSelected()) return false;
    
// Work around nonsensical selection resetting in IE9/10
    
if (ie && !ie_upto8 && cm.display.inputHasSelection === text) {
      
resetInput(cm);
      return 
false;
    }

    var 
withOp = !cm.curOp;
    if (
withOpstartOperation(cm);
    
cm.display.shift false;

    if (
text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput)
      
prevInput "u200b";
    
// Find the part of the input that is actually new
    
var same 0Math.min(prevInput.lengthtext.length);
    while (
same && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
    var 
inserted text.slice(same), textLines splitLines(inserted);

    
// When pasing N lines into N selections, insert one line per selection
    
var multiPaste cm.state.pasteIncoming && textLines.length && doc.sel.ranges.length == textLines.length;

    
// Normal behavior is to insert the new text into every selection
    
for (var doc.sel.ranges.length 1>= 0i--) {
      var 
range doc.sel.ranges[i];
      var 
from range.from(), to range.to();
      
// Handle deletion
      
if (same prevInput.length)
        
from Pos(from.linefrom.ch - (prevInput.length same));
      
// Handle overwrite
      
else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
        
to Pos(to.lineMath.min(getLine(docto.line).text.lengthto.ch lst(textLines).length));
      var 
updateInput cm.curOp.updateInput;
      var 
changeEvent = {fromfromtototextmultiPaste ? [textLines[i]] : textLines,
                         
origincm.state.pasteIncoming "paste" cm.state.cutIncoming "cut" "+input"};
      
makeChange(cm.docchangeEvent);
      
signalLater(cm"inputRead"cmchangeEvent);
      
// When an 'electric' character is inserted, immediately trigger a reindent
      
if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
          
cm.options.smartIndent && range.head.ch 100 &&
          (!
|| doc.sel.ranges[1].head.line != range.head.line)) {
        var 
mode cm.getModeAt(range.head);
        if (
mode.electricChars) {
          for (var 
0mode.electricChars.lengthj++)
            if (
inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
              
indentLine(cmrange.head.line"smart");
              break;
            }
        } else if (
mode.electricInput) {
          var 
end changeEnd(changeEvent);
          if (
mode.electricInput.test(getLine(docend.line).text.slice(0end.ch)))
            
indentLine(cmrange.head.line"smart");
        }
      }
    }
    
ensureCursorVisible(cm);
    
cm.curOp.updateInput updateInput;
    
cm.curOp.typing true;

    
// Don't leave long text in the textarea, since it makes further polling slow
    
if (text.length 1000 || text.indexOf("n") > -1input.value cm.display.prevInput "";
    else 
cm.display.prevInput text;
    if (
withOpendOperation(cm);
    
cm.state.pasteIncoming cm.state.cutIncoming false;
    return 
true;
  }

  
// Reset the input to correspond to the selection (or to be empty,
  // when not typing and nothing is selected)
  
function resetInput(cmtyping) {
    var 
minimalselecteddoc cm.doc;
    if (
cm.somethingSelected()) {
      
cm.display.prevInput "";
      var 
range doc.sel.primary();
      
minimal hasCopyEvent &&
        (
range.to().line range.from().line 100 || (selected cm.getSelection()).length 1000);
      var 
content minimal "-" selected || cm.getSelection();
      
cm.display.input.value content;
      if (
cm.state.focusedselectInput(cm.display.input);
      if (
ie && !ie_upto8cm.display.inputHasSelection content;
    } else if (!
typing) {
      
cm.display.prevInput cm.display.input.value "";
      if (
ie && !ie_upto8cm.display.inputHasSelection null;
    }
    
cm.display.inaccurateSelection minimal;
  }

  function 
focusInput(cm) {
    if (
cm.options.readOnly != "nocursor" && (!mobile || activeElt() != cm.display.input))
      
cm.display.input.focus();
  }

  function 
ensureFocus(cm) {
    if (!
cm.state.focused) { focusInput(cm); onFocus(cm); }
  }

  function 
isReadOnly(cm) {
    return 
cm.options.readOnly || cm.doc.cantEdit;
  }

  
// EVENT HANDLERS

  // Attach the necessary event handlers when initializing the editor
  
function registerEventHandlers(cm) {
    var 
cm.display;
    
on(d.scroller"mousedown"operation(cmonMouseDown));
    
// Older IE's will not fire a second mousedown for a double click
    
if (ie_upto10)
      
on(d.scroller"dblclick"operation(cm, function(e) {
        if (
signalDOMEvent(cme)) return;
        var 
pos posFromMouse(cme);
        if (!
pos || clickInGutter(cme) || eventInWidget(cm.displaye)) return;
        
e_preventDefault(e);
        var 
word findWordAt(cmpos);
        
extendSelection(cm.docword.anchorword.head);
      }));
    else
      
on(d.scroller"dblclick", function(e) { signalDOMEvent(cme) || e_preventDefault(e); });
    
// Prevent normal selection in the editor (we handle our own)
    
on(d.lineSpace"selectstart", function(e) {
      if (!
eventInWidget(de)) e_preventDefault(e);
    });
    
// Some browsers fire contextmenu *after* opening the menu, at
    // which point we can't mess with it anymore. Context menu is
    // handled in onMouseDown for these browsers.
    
if (!captureRightClickon(d.scroller"contextmenu", function(e) {onContextMenu(cme);});

    
// Sync scrolling between fake scrollbars and real scrollable
    // area, ensure viewport is updated when scrolling.
    
on(d.scroller"scroll", function() {
      if (
d.scroller.clientHeight) {
        
setScrollTop(cmd.scroller.scrollTop);
        
setScrollLeft(cmd.scroller.scrollLefttrue);
        
signal(cm"scroll"cm);
      }
    });
    
on(d.scrollbarV"scroll", function() {
      if (
d.scroller.clientHeightsetScrollTop(cmd.scrollbarV.scrollTop);
    });
    
on(d.scrollbarH"scroll", function() {
      if (
d.scroller.clientHeightsetScrollLeft(cmd.scrollbarH.scrollLeft);
    });

    
// Listen to wheel events in order to try and update the viewport on time.
    
on(d.scroller"mousewheel", function(e){onScrollWheel(cme);});
    
on(d.scroller"DOMMouseScroll", function(e){onScrollWheel(cme);});

    
// Prevent clicks in the scrollbars from killing focus
    
function reFocus() { if (cm.state.focusedsetTimeout(bind(focusInputcm), 0); }
    
on(d.scrollbarH"mousedown"reFocus);
    
on(d.scrollbarV"mousedown"reFocus);
    
// Prevent wrapper from ever scrolling
    
on(d.wrapper"scroll", function() { d.wrapper.scrollTop d.wrapper.scrollLeft 0; });

    
on(d.input"keyup"operation(cmonKeyUp));
    
on(d.input"input", function() {
      if (
ie && !ie_upto8 && cm.display.inputHasSelectioncm.display.inputHasSelection null;
      
fastPoll(cm);
    });
    
on(d.input"keydown"operation(cmonKeyDown));
    
on(d.input"keypress"operation(cmonKeyPress));
    
on(d.input"focus"bind(onFocuscm));
    
on(d.input"blur"bind(onBlurcm));

    function 
drag_(e) {
      if (!
signalDOMEvent(cme)) e_stop(e);
    }
    if (
cm.options.dragDrop) {
      
on(d.scroller"dragstart", function(e){onDragStart(cme);});
      
on(d.scroller"dragenter"drag_);
      
on(d.scroller"dragover"drag_);
      
on(d.scroller"drop"operation(cmonDrop));
    }
    
on(d.scroller"paste", function(e) {
      if (
eventInWidget(de)) return;
      
cm.state.pasteIncoming true;
      
focusInput(cm);
      
fastPoll(cm);
    });
    
on(d.input"paste", function() {
      
// Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
      // Add a char to the end of textarea before paste occur so that
      // selection doesn't span to the end of textarea.
      
if (webkit && !cm.state.fakedLastChar && !(new Date cm.state.lastMiddleDown 200)) {
        var 
start d.input.selectionStartend d.input.selectionEnd;
        
d.input.value += "$";
        
d.input.selectionStart start;
        
d.input.selectionEnd end;
        
cm.state.fakedLastChar true;
      }
      
cm.state.pasteIncoming true;
      
fastPoll(cm);
    });

    function 
prepareCopyCut(e) {
      if (
cm.somethingSelected()) {
        if (
d.inaccurateSelection) {
          
d.prevInput "";
          
d.inaccurateSelection false;
          
d.input.value cm.getSelection();
          
selectInput(d.input);
        }
      } else {
        var 
text ""ranges = [];
        for (var 
0cm.doc.sel.ranges.lengthi++) {
          var 
line cm.doc.sel.ranges[i].head.line;
          var 
lineRange = {anchorPos(line0), headPos(line 10)};
          
ranges.push(lineRange);
          
text += cm.getRange(lineRange.anchorlineRange.head);
        }
        if (
e.type == "cut") {
          
cm.setSelections(rangesnullsel_dontScroll);
        } else {
          
d.prevInput "";
          
d.input.value text;
          
selectInput(d.input);
        }
      }
      if (
e.type == "cut"cm.state.cutIncoming true;
    }
    
on(d.input"cut"prepareCopyCut);
    
on(d.input"copy"prepareCopyCut);

    
// Needed to handle Tab key in KHTML
    
if (khtmlon(d.sizer"mouseup", function() {
      if (
activeElt() == d.inputd.input.blur();
      
focusInput(cm);
    });
  }

  
// Called when the window resizes
  
function onResize(cm) {
    
// Might be a text scaling operation, clear size caches.
    
var cm.display;
    
d.cachedCharWidth d.cachedTextHeight d.cachedPaddingH null;
    
cm.setSize();
  }

  
// MOUSE EVENTS

  // Return true when the given mouse event happened in a widget
  
function eventInWidget(displaye) {
    for (var 
e_target(e); != display.wrappern.parentNode) {
      if (!
|| n.ignoreEvents || n.parentNode == display.sizer && != display.mover) return true;
    }
  }

  
// Given a mouse event, find the corresponding position. If liberal
  // is false, it checks whether a gutter or scrollbar was clicked,
  // and returns null if it was. forRect is used by rectangular
  // selections, and tries to estimate a character position even for
  // coordinates beyond the right of the text.
  
function posFromMouse(cmeliberalforRect) {
    var 
display cm.display;
    if (!
liberal) {
      var 
target e_target(e);
      if (
target == display.scrollbarH || target == display.scrollbarV ||
          
target == display.scrollbarFiller || target == display.gutterFiller) return null;
    }
    var 
xyspace display.lineSpace.getBoundingClientRect();
    
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
    
try { e.clientX space.lefte.clientY space.top; }
    catch (
e) { return null; }
    var 
coords coordsChar(cmxy), line;
    if (
forRect && coords.xRel == && (line getLine(cm.doccoords.line).text).length == coords.ch) {
      var 
colDiff countColumn(lineline.lengthcm.options.tabSize) - line.length;
      
coords Pos(coords.lineMath.max(0Math.round((paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
    }
    return 
coords;
  }

  
// A mouse down can be a single click, double click, triple click,
  // start of selection drag, start of text drag, new cursor
  // (ctrl-click), rectangle drag (alt-drag), or xwin
  // middle-click-paste. Or it might be a click on something we should
  // not interfere with, such as a scrollbar or widget.
  
function onMouseDown(e) {
    if (
signalDOMEvent(thise)) return;
    var 
cm thisdisplay cm.display;
    
display.shift e.shiftKey;

    if (
eventInWidget(displaye)) {
      if (!
webkit) {
        
// Briefly turn off draggability, to allow widgets to do
        // normal dragging things.
        
display.scroller.draggable false;
        
setTimeout(function(){display.scroller.draggable true;}, 100);
      }
      return;
    }
    if (
clickInGutter(cme)) return;
    var 
start posFromMouse(cme);
    
window.focus();

    switch (
e_button(e)) {
    case 
1:
      if (
start)
        
leftButtonDown(cmestart);
      else if (
e_target(e) == display.scroller)
        
e_preventDefault(e);
      break;
    case 
2:
      if (
webkitcm.state.lastMiddleDown = +new Date;
      if (
startextendSelection(cm.docstart);
      
setTimeout(bind(focusInputcm), 20);
      
e_preventDefault(e);
      break;
    case 
3:
      if (
captureRightClickonContextMenu(cme);
      break;
    }
  }

  var 
lastClicklastDoubleClick;
  function 
leftButtonDown(cmestart) {
    
setTimeout(bind(ensureFocuscm), 0);

    var 
now = +new Datetype;
    if (
lastDoubleClick && lastDoubleClick.time now 400 && cmp(lastDoubleClick.posstart) == 0) {
      
type "triple";
    } else if (
lastClick && lastClick.time now 400 && cmp(lastClick.posstart) == 0) {
      
type "double";
      
lastDoubleClick = {timenowposstart};
    } else {
      
type "single";
      
lastClick = {timenowposstart};
    }

    var 
sel cm.doc.selmodifier mac e.metaKey e.ctrlKey;
    if (
cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
        
type == "single" && sel.contains(start) > -&& sel.somethingSelected())
      
leftButtonStartDrag(cmestartmodifier);
    else
      
leftButtonSelect(cmestarttypemodifier);
  }

  
// Start a text drag. When it ends, see if any dragging actually
  // happen, and treat as a click if it didn't.
  
function leftButtonStartDrag(cmestartmodifier) {
    var 
display cm.display;
    var 
dragEnd operation(cm, function(e2) {
      if (
webkitdisplay.scroller.draggable false;
      
cm.state.draggingText false;
      
off(document"mouseup"dragEnd);
      
off(display.scroller"drop"dragEnd);
      if (
Math.abs(e.clientX e2.clientX) + Math.abs(e.clientY e2.clientY) < 10) {
        
e_preventDefault(e2);
        if (!
modifier)
          
extendSelection(cm.docstart);
        
focusInput(cm);
        
// Work around unexplainable focus problem in IE9 (#2127)
        
if (ie_upto10 && !ie_upto8)
          
setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
      }
    });
    
// Let the drag handler handle this.
    
if (webkitdisplay.scroller.draggable true;
    
cm.state.draggingText dragEnd;
    
// IE's approach to draggable
    
if (display.scroller.dragDropdisplay.scroller.dragDrop();
    
on(document"mouseup"dragEnd);
    
on(display.scroller"drop"dragEnd);
  }

  
// Normal selection, as opposed to text dragging.
  
function leftButtonSelect(cmestarttypeaddNew) {
    var 
display cm.displaydoc cm.doc;
    
e_preventDefault(e);

    var 
ourRangeourIndexstartSel doc.sel;
    if (
addNew && !e.shiftKey) {
      
ourIndex doc.sel.contains(start);
      if (
ourIndex > -1)
        
ourRange doc.sel.ranges[ourIndex];
      else
        
ourRange = new Range(startstart);
    } else {
      
ourRange doc.sel.primary();
    }

    if (
e.altKey) {
      
type "rect";
      if (!
addNewourRange = new Range(startstart);
      
start posFromMouse(cmetruetrue);
      
ourIndex = -1;
    } else if (
type == "double") {
      var 
word findWordAt(cmstart);
      if (
cm.display.shift || doc.extend)
        
ourRange extendRange(docourRangeword.anchorword.head);
      else
        
ourRange word;
    } else if (
type == "triple") {
      var 
line = new Range(Pos(start.line0), clipPos(docPos(start.line 10)));
      if (
cm.display.shift || doc.extend)
        
ourRange extendRange(docourRangeline.anchorline.head);
      else
        
ourRange line;
    } else {
      
ourRange extendRange(docourRangestart);
    }

    if (!
addNew) {
      
ourIndex 0;
      
setSelection(doc, new Selection([ourRange], 0), sel_mouse);
      
startSel doc.sel;
    } else if (
ourIndex > -1) {
      
replaceOneSelection(docourIndexourRangesel_mouse);
    } else {
      
ourIndex doc.sel.ranges.length;
      
setSelection(docnormalizeSelection(doc.sel.ranges.concat([ourRange]), ourIndex),
                   {
scrollfalseorigin"*mouse"});
    }

    var 
lastPos start;
    function 
extendTo(pos) {
      if (
cmp(lastPospos) == 0) return;
      
lastPos pos;

      if (
type == "rect") {
        var 
ranges = [], tabSize cm.options.tabSize;
        var 
startCol countColumn(getLine(docstart.line).textstart.chtabSize);
        var 
posCol countColumn(getLine(docpos.line).textpos.chtabSize);
        var 
left Math.min(startColposCol), right Math.max(startColposCol);
        for (var 
line Math.min(start.linepos.line), end Math.min(cm.lastLine(), Math.max(start.linepos.line));
             
line <= endline++) {
          var 
text getLine(docline).textleftPos findColumn(textlefttabSize);
          if (
left == right)
            
ranges.push(new Range(Pos(lineleftPos), Pos(lineleftPos)));
          else if (
text.length leftPos)
            
ranges.push(new Range(Pos(lineleftPos), Pos(linefindColumn(textrighttabSize))));
        }
        if (!
ranges.lengthranges.push(new Range(startstart));
        
setSelection(docnormalizeSelection(startSel.ranges.slice(0ourIndex).concat(ranges), ourIndex),
                     {
origin"*mouse"scrollfalse});
        
cm.scrollIntoView(pos);
      } else {
        var 
oldRange ourRange;
        var 
anchor oldRange.anchorhead pos;
        if (
type != "single") {
          if (
type == "double")
            var 
range findWordAt(cmpos);
          else
            var 
range = new Range(Pos(pos.line0), clipPos(docPos(pos.line 10)));
          if (
cmp(range.anchoranchor) > 0) {
            
head range.head;
            
anchor minPos(oldRange.from(), range.anchor);
          } else {
            
head range.anchor;
            
anchor maxPos(oldRange.to(), range.head);
          }
        }
        var 
ranges startSel.ranges.slice(0);
        
ranges[ourIndex] = new Range(clipPos(docanchor), head);
        
setSelection(docnormalizeSelection(rangesourIndex), sel_mouse);
      }
    }

    var 
editorSize display.wrapper.getBoundingClientRect();
    
// Used to ensure timeout re-tries don't fire when another extend
    // happened in the meantime (clearTimeout isn't reliable -- at
    // least on Chrome, the timeouts still happen even when cleared,
    // if the clear happens after their scheduled firing time).
    
var counter 0;

    function 
extend(e) {
      var 
curCount = ++counter;
      var 
cur posFromMouse(cmetruetype == "rect");
      if (!
cur) return;
      if (
cmp(curlastPos) != 0) {
        
ensureFocus(cm);
        
extendTo(cur);
        var 
visible visibleLines(displaydoc);
        if (
cur.line >= visible.to || cur.line visible.from)
          
setTimeout(operation(cm, function(){if (counter == curCountextend(e);}), 150);
      } else {
        var 
outside e.clientY editorSize.top ? -20 e.clientY editorSize.bottom 20 0;
        if (
outsidesetTimeout(operation(cm, function() {
          if (
counter != curCount) return;
          
display.scroller.scrollTop += outside;
          
extend(e);
        }), 
50);
      }
    }

    function 
done(e) {
      
counter Infinity;
      
e_preventDefault(e);
      
focusInput(cm);
      
off(document"mousemove"move);
      
off(document"mouseup"up);
      
doc.history.lastSelOrigin null;
    }

    var 
move operation(cm, function(e) {
      if ((
ie && !ie_upto9) ?  !e.buttons : !e_button(e)) done(e);
      else 
extend(e);
    });
    var 
up operation(cmdone);
    
on(document"mousemove"move);
    
on(document"mouseup"up);
  }

  
// Determines whether an event happened in the gutter, and fires the
  // handlers for the corresponding event.
  
function gutterEvent(cmetypepreventsignalfn) {
    try { var 
mX e.clientXmY e.clientY; }
    catch(
e) { return false; }
    if (
mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
    if (
prevente_preventDefault(e);

    var 
display cm.display;
    var 
lineBox display.lineDiv.getBoundingClientRect();

    if (
mY lineBox.bottom || !hasHandler(cmtype)) return e_defaultPrevented(e);
    
mY -= lineBox.top display.viewOffset;

    for (var 
0cm.options.gutters.length; ++i) {
      var 
display.gutters.childNodes[i];
      if (
&& g.getBoundingClientRect().right >= mX) {
        var 
line lineAtHeight(cm.docmY);
        var 
gutter cm.options.gutters[i];
        
signalfn(cmtypecmlineguttere);
        return 
e_defaultPrevented(e);
      }
    }
  }

  function 
clickInGutter(cme) {
    return 
gutterEvent(cme"gutterClick"truesignalLater);
  }

  
// Kludge to work around strange IE behavior where it'll sometimes
  // re-fire a series of drag-related events right after the drop (#1551)
  
var lastDrop 0;

  function 
onDrop(e) {
    var 
cm this;
    if (
signalDOMEvent(cme) || eventInWidget(cm.displaye))
      return;
    
e_preventDefault(e);
    if (
ielastDrop = +new Date;
    var 
pos posFromMouse(cmetrue), files e.dataTransfer.files;
    if (!
pos || isReadOnly(cm)) return;
    
// Might be a file drop, in which case we simply extract the text
    // and insert it.
    
if (files && files.length && window.FileReader && window.File) {
      var 
files.lengthtext = Array(n), read 0;
      var 
loadFile = function(filei) {
        var 
reader = new FileReader;
        
reader.onload operation(cm, function() {
          
text[i] = reader.result;
          if (++
read == n) {
            
pos clipPos(cm.docpos);
            var 
change = {frompostopostextsplitLines(text.join("n")), origin"paste"};
            
makeChange(cm.docchange);
            
setSelectionReplaceHistory(cm.docsimpleSelection(poschangeEnd(change)));
          }
        });
        
reader.readAsText(file);
      };
      for (var 
0n; ++iloadFile(files[i], i);
    } else { 
// Normal drop
      // Don't do a replace if the drop happened inside of the selected text.
      
if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
        
cm.state.draggingText(e);
        
// Ensure the editor is re-focused
        
setTimeout(bind(focusInputcm), 20);
        return;
      }
      try {
        var 
text e.dataTransfer.getData("Text");
        if (
text) {
          if (
cm.state.draggingText && !(mac e.metaKey e.ctrlKey))
            var 
selected cm.listSelections();
          
setSelectionNoUndo(cm.docsimpleSelection(pospos));
          if (
selected) for (var 0selected.length; ++i)
            
replaceRange(cm.doc""selected[i].anchorselected[i].head"drag");
          
cm.replaceSelection(text"around""paste");
          
focusInput(cm);
        }
      }
      catch(
e){}
    }
  }

  function 
onDragStart(cme) {
    if (
ie && (!cm.state.draggingText || +new Date lastDrop 100)) { e_stop(e); return; }
    if (
signalDOMEvent(cme) || eventInWidget(cm.displaye)) return;

    
e.dataTransfer.setData("Text"cm.getSelection());

    
// Use dummy image instead of default browsers image.
    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
    
if (e.dataTransfer.setDragImage && !safari) {
      var 
img elt("img"nullnull"position: fixed; left: 0; top: 0;");
      
img.src "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
      if (
presto) {
        
img.width img.height 1;
        
cm.display.wrapper.appendChild(img);
        
// Force a relayout, or Opera won't use our image for some obscure reason
        
img._top img.offsetTop;
      }
      
e.dataTransfer.setDragImage(img00);
      if (
prestoimg.parentNode.removeChild(img);
    }
  }

  
// SCROLL EVENTS

  // Sync the scrollable area and scrollbars, ensure the viewport
  // covers the visible area.
  
function setScrollTop(cmval) {
    if (
Math.abs(cm.doc.scrollTop val) < 2) return;
    
cm.doc.scrollTop val;
    if (!
geckoupdateDisplay(cm, {topval});
    if (
cm.display.scroller.scrollTop != valcm.display.scroller.scrollTop val;
    if (
cm.display.scrollbarV.scrollTop != valcm.display.scrollbarV.scrollTop val;
    if (
geckoupdateDisplay(cm);
    
startWorker(cm100);
  }
  
// Sync scroller and scrollbar, ensure the gutter elements are
  // aligned.
  
function setScrollLeft(cmvalisScroller) {
    if (
isScroller val == cm.doc.scrollLeft Math.abs(cm.doc.scrollLeft val) < 2) return;
    
val Math.min(valcm.display.scroller.scrollWidth cm.display.scroller.clientWidth);
    
cm.doc.scrollLeft val;
    
alignHorizontally(cm);
    if (
cm.display.scroller.scrollLeft != valcm.display.scroller.scrollLeft val;
    if (
cm.display.scrollbarH.scrollLeft != valcm.display.scrollbarH.scrollLeft val;
  }

  
// Since the delta values reported on mouse wheel events are
  // unstandardized between browsers and even browser versions, and
  // generally horribly unpredictable, this code starts by measuring
  // the scroll effect that the first few mouse wheel events have,
  // and, from that, detects the way it can convert deltas to pixel
  // offsets afterwards.
  //
  // The reason we want to know the amount a wheel event will scroll
  // is that it gives us a chance to update the display before the
  // actual scrolling happens, reducing flickering.

  
var wheelSamples 0wheelPixelsPerUnit null;
  
// Fill in a browser-detected starting value on browsers where we
  // know one. These don't have to be accurate -- the result of them
  // being wrong would just be a slight flicker on the first wheel
  // scroll (if it is large enough).
  
if (iewheelPixelsPerUnit = -.53;
  else if (
geckowheelPixelsPerUnit 15;
  else if (
chromewheelPixelsPerUnit = -.7;
  else if (
safariwheelPixelsPerUnit = -1/3;

  function 
onScrollWheel(cme) {
    var 
dx e.wheelDeltaXdy e.wheelDeltaY;
    if (
dx == null && e.detail && e.axis == e.HORIZONTAL_AXISdx e.detail;
    if (
dy == null && e.detail && e.axis == e.VERTICAL_AXISdy e.detail;
    else if (
dy == nulldy e.wheelDelta;

    var 
display cm.displayscroll display.scroller;
    
// Quit if there's nothing to scroll here
    
if (!(dx && scroll.scrollWidth scroll.clientWidth ||
          
dy && scroll.scrollHeight scroll.clientHeight)) return;

    
// Webkit browsers on OS X abort momentum scrolls when the target
    // of the scroll event is removed from the scrollable element.
    // This hack (see related code in patchDisplay) makes sure the
    // element is kept around.
    
if (dy && mac && webkit) {
      
outer: for (var cur e.targetview display.viewcur != scrollcur cur.parentNode) {
        for (var 
0view.lengthi++) {
          if (
view[i].node == cur) {
            
cm.display.currentWheelTarget cur;
            break 
outer;
          }
        }
      }
    }

    
// On some browsers, horizontal scrolling will cause redraws to
    // happen before the gutter has been realigned, causing it to
    // wriggle around in a most unseemly way. When we have an
    // estimated pixels/delta value, we just handle horizontal
    // scrolling entirely here. It'll be slightly off from native, but
    // better than glitching out.
    
if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
      if (
dy)
        
setScrollTop(cmMath.max(0Math.min(scroll.scrollTop dy wheelPixelsPerUnitscroll.scrollHeight scroll.clientHeight)));
      
setScrollLeft(cmMath.max(0Math.min(scroll.scrollLeft dx wheelPixelsPerUnitscroll.scrollWidth scroll.clientWidth)));
      
e_preventDefault(e);
      
display.wheelStartX null// Abort measurement, if in progress
      
return;
    }

    
// 'Project' the visible viewport to cover the area that is being
    // scrolled into view (if we know enough to estimate it).
    
if (dy && wheelPixelsPerUnit != null) {
      var 
pixels dy wheelPixelsPerUnit;
      var 
top cm.doc.scrollTopbot top display.wrapper.clientHeight;
      if (
pixels 0top Math.max(0top pixels 50);
      else 
bot Math.min(cm.doc.heightbot pixels 50);
      
updateDisplay(cm, {toptopbottombot});
    }

    if (
wheelSamples 20) {
      if (
display.wheelStartX == null) {
        
display.wheelStartX scroll.scrollLeftdisplay.wheelStartY scroll.scrollTop;
        
display.wheelDX dxdisplay.wheelDY dy;
        
setTimeout(function() {
          if (
display.wheelStartX == null) return;
          var 
movedX scroll.scrollLeft display.wheelStartX;
          var 
movedY scroll.scrollTop display.wheelStartY;
          var 
sample = (movedY && display.wheelDY && movedY display.wheelDY) ||
            (
movedX && display.wheelDX && movedX display.wheelDX);
          
display.wheelStartX display.wheelStartY null;
          if (!
sample) return;
          
wheelPixelsPerUnit = (wheelPixelsPerUnit wheelSamples sample) / (wheelSamples 1);
          ++
wheelSamples;
        }, 
200);
      } else {
        
display.wheelDX += dxdisplay.wheelDY += dy;
      }
    }
  }

  
// KEY EVENTS

  // Run a handler that was bound to a key.
  
function doHandleBinding(cmbounddropShift) {
    if (
typeof bound == "string") {
      
bound commands[bound];
      if (!
bound) return false;
    }
    
// Ensure previous input has been read, so that the handler sees a
    // consistent view of the document
    
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast false;
    var 
prevShift cm.display.shiftdone false;
    try {
      if (
isReadOnly(cm)) cm.state.suppressEdits true;
      if (
dropShiftcm.display.shift false;
      
done bound(cm) != Pass;
    } finally {
      
cm.display.shift prevShift;
      
cm.state.suppressEdits false;
    }
    return 
done;
  }

  
// Collect the currently active keymaps.
  
function allKeyMaps(cm) {
    var 
maps cm.state.keyMaps.slice(0);
    if (
cm.options.extraKeysmaps.push(cm.options.extraKeys);
    
maps.push(cm.options.keyMap);
    return 
maps;
  }

  var 
maybeTransition;
  
// Handle a key from the keydown event.
  
function handleKeyBinding(cme) {
    
// Handle automatic keymap transitions
    
var startMap getKeyMap(cm.options.keyMap), next startMap.auto;
    
clearTimeout(maybeTransition);
    if (
next && !isModifierKey(e)) maybeTransition setTimeout(function() {
      if (
getKeyMap(cm.options.keyMap) == startMap) {
        
cm.options.keyMap = (next.call next.call(nullcm) : next);
        
keyMapChanged(cm);
      }
    }, 
50);

    var 
name keyName(etrue), handled false;
    if (!
name) return false;
    var 
keymaps allKeyMaps(cm);

    if (
e.shiftKey) {
      
// First try to resolve full name (including 'Shift-'). Failing
      // that, see if there is a cursor-motion command (starting with
      // 'go') bound to the keyname without 'Shift-'.
      
handled lookupKey("Shift-" namekeymaps, function(b) {return doHandleBinding(cmbtrue);})
             || 
lookupKey(namekeymaps, function(b) {
                  if (
typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
                    return 
doHandleBinding(cmb);
                });
    } else {
      
handled lookupKey(namekeymaps, function(b) { return doHandleBinding(cmb); });
    }

    if (
handled) {
      
e_preventDefault(e);
      
restartBlink(cm);
      
signalLater(cm"keyHandled"cmnamee);
    }
    return 
handled;
  }

  
// Handle a key from the keypress event
  
function handleCharBinding(cmech) {
    var 
handled lookupKey("'" ch "'"allKeyMaps(cm),
                            function(
b) { return doHandleBinding(cmbtrue); });
    if (
handled) {
      
e_preventDefault(e);
      
restartBlink(cm);
      
signalLater(cm"keyHandled"cm"'" ch "'"e);
    }
    return 
handled;
  }

  var 
lastStoppedKey null;
  function 
onKeyDown(e) {
    var 
cm this;
    
ensureFocus(cm);
    if (
signalDOMEvent(cme)) return;
    
// IE does strange things with escape.
    
if (ie_upto10 && e.keyCode == 27e.returnValue false;
    var 
code e.keyCode;
    
cm.display.shift code == 16 || e.shiftKey;
    var 
handled handleKeyBinding(cme);
    if (
presto) {
      
lastStoppedKey handled code null;
      
// Opera has no cut event... we try to at least catch the key combo
      
if (!handled && code == 88 && !hasCopyEvent && (mac e.metaKey e.ctrlKey))
        
cm.replaceSelection(""null"cut");
    }

    
// Turn mouse into crosshair when Alt is held on Mac.
    
if (code == 18 && !/bCodeMirror-crosshairb/.test(cm.display.lineDiv.className))
      
showCrossHair(cm);
  }

  function 
showCrossHair(cm) {
    var 
lineDiv cm.display.lineDiv;
    
addClass(lineDiv"CodeMirror-crosshair");

    function 
up(e) {
      if (
e.keyCode == 18 || !e.altKey) {
        
rmClass(lineDiv"CodeMirror-crosshair");
        
off(document"keyup"up);
        
off(document"mouseover"up);
      }
    }
    
on(document"keyup"up);
    
on(document"mouseover"up);
  }

  function 
onKeyUp(e) {
    if (
signalDOMEvent(thise)) return;
    if (
e.keyCode == 16this.doc.sel.shift false;
  }

  function 
onKeyPress(e) {
    var 
cm this;
    if (
signalDOMEvent(cme)) return;
    var 
keyCode e.keyCodecharCode e.charCode;
    if (
presto && keyCode == lastStoppedKey) {lastStoppedKey nulle_preventDefault(e); return;}
    if (((
presto && (!e.which || e.which 10)) || khtml) && handleKeyBinding(cme)) return;
    var 
ch String.fromCharCode(charCode == null keyCode charCode);
    if (
handleCharBinding(cmech)) return;
    if (
ie && !ie_upto8cm.display.inputHasSelection null;
    
fastPoll(cm);
  }

  
// FOCUS/BLUR EVENTS

  
function onFocus(cm) {
    if (
cm.options.readOnly == "nocursor") return;
    if (!
cm.state.focused) {
      
signal(cm"focus"cm);
      
cm.state.focused true;
      
addClass(cm.display.wrapper"CodeMirror-focused");
      
// The prevInput test prevents this from firing when a context
      // menu is closed (since the resetInput would kill the
      // select-all detection hack)
      
if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
        
resetInput(cm);
        if (
webkitsetTimeout(bind(resetInputcmtrue), 0); // Issue #1730
      
}
    }
    
slowPoll(cm);
    
restartBlink(cm);
  }
  function 
onBlur(cm) {
    if (
cm.state.focused) {
      
signal(cm"blur"cm);
      
cm.state.focused false;
      
rmClass(cm.display.wrapper"CodeMirror-focused");
    }
    
clearInterval(cm.display.blinker);
    
setTimeout(function() {if (!cm.state.focusedcm.display.shift false;}, 150);
  }

  
// CONTEXT MENU HANDLING

  // To make the context menu work, we need to briefly unhide the
  // textarea (making it as unobtrusive as possible) to let the
  // right-click take effect on it.
  
function onContextMenu(cme) {
    if (
signalDOMEvent(cme"contextmenu")) return;
    var 
display cm.display;
    if (
eventInWidget(displaye) || contextMenuInGutter(cme)) return;

    var 
pos posFromMouse(cme), scrollPos display.scroller.scrollTop;
    if (!
pos || presto) return; // Opera is difficult.

    // Reset the current text selection only if the click is done outside of the selection
    // and 'resetSelectionOnContextMenu' option is true.
    
var reset cm.options.resetSelectionOnContextMenu;
    if (
reset && cm.doc.sel.contains(pos) == -1)
      
operation(cmsetSelection)(cm.docsimpleSelection(pos), sel_dontScroll);

    var 
oldCSS display.input.style.cssText;
    
display.inputDiv.style.position "absolute";
    
display.input.style.cssText "position: fixed; width: 30px; height: 30px; top: " + (e.clientY 5) +
      
"px; left: " + (e.clientX 5) + "px; z-index: 1000; background: " +
      (
ie "rgba(255, 255, 255, .05)" "transparent") +
      
"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
    
focusInput(cm);
    
resetInput(cm);
    
// Adds "Select all" to context menu in FF
    
if (!cm.somethingSelected()) display.input.value display.prevInput " ";
    
display.selForContextMenu cm.doc.sel;
    
clearTimeout(display.detectingSelectAll);

    
// Select-all will be greyed out if there's nothing to select, so
    // this adds a zero-width space so that we can later check whether
    // it got selected.
    
function prepareSelectAllHack() {
      if (
display.input.selectionStart != null) {
        var 
selected cm.somethingSelected();
        var 
extval display.input.value "u200b" + (selected display.input.value "");
        
display.prevInput selected "" "u200b";
        
display.input.selectionStart 1display.input.selectionEnd extval.length;
        
// Re-set this, in case some other handler touched the
        // selection in the meantime.
        
display.selForContextMenu cm.doc.sel;
      }
    }
    function 
rehide() {
      
display.inputDiv.style.position "relative";
      
display.input.style.cssText oldCSS;
      if (
ie_upto8display.scrollbarV.scrollTop display.scroller.scrollTop scrollPos;
      
slowPoll(cm);

      
// Try to detect the user choosing select-all
      
if (display.input.selectionStart != null) {
        if (!
ie || ie_upto8prepareSelectAllHack();
        var 
0poll = function() {
          if (
display.selForContextMenu == cm.doc.sel && display.input.selectionStart == 0)
            
operation(cmcommands.selectAll)(cm);
          else if (
i++ < 10display.detectingSelectAll setTimeout(poll500);
          else 
resetInput(cm);
        };
        
display.detectingSelectAll setTimeout(poll200);
      }
    }

    if (
ie && !ie_upto8prepareSelectAllHack();
    if (
captureRightClick) {
      
e_stop(e);
      var 
mouseup = function() {
        
off(window"mouseup"mouseup);
        
setTimeout(rehide20);
      };
      
on(window"mouseup"mouseup);
    } else {
      
setTimeout(rehide50);
    }
  }

  function 
contextMenuInGutter(cme) {
    if (!
hasHandler(cm"gutterContextMenu")) return false;
    return 
gutterEvent(cme"gutterContextMenu"falsesignal);
  }

  
// UPDATING

  // Compute the position of the end of a change (its 'to' property
  // refers to the pre-change end).
  
var changeEnd CodeMirror.changeEnd = function(change) {
    if (!
change.text) return change.to;
    return 
Pos(change.from.line change.text.length 1,
               
lst(change.text).length + (change.text.length == change.from.ch 0));
  };

  
// Adjust a position to refer to the post-change position of the
  // same text, or the end of the change if the change covers it.
  
function adjustForChange(poschange) {
    if (
cmp(poschange.from) < 0) return pos;
    if (
cmp(poschange.to) <= 0) return changeEnd(change);

    var 
line pos.line change.text.length - (change.to.line change.from.line) - 1ch pos.ch;
    if (
pos.line == change.to.linech += changeEnd(change).ch change.to.ch;
    return 
Pos(linech);
  }

  function 
computeSelAfterChange(docchange) {
    var 
out = [];
    for (var 
0doc.sel.ranges.lengthi++) {
      var 
range doc.sel.ranges[i];
      
out.push(new Range(adjustForChange(range.anchorchange),
                         
adjustForChange(range.headchange)));
    }
    return 
normalizeSelection(outdoc.sel.primIndex);
  }

  function 
offsetPos(posoldnw) {
    if (
pos.line == old.line)
      return 
Pos(nw.linepos.ch old.ch nw.ch);
    else
      return 
Pos(nw.line + (pos.line old.line), pos.ch);
  }

  
// Used by replaceSelections to allow moving the selection to the
  // start or around the replaced test. Hint may be "start" or "around".
  
function computeReplacedSel(docchangeshint) {
    var 
out = [];
    var 
oldPrev Pos(doc.first0), newPrev oldPrev;
    for (var 
0changes.lengthi++) {
      var 
change changes[i];
      var 
from offsetPos(change.fromoldPrevnewPrev);
      var 
to offsetPos(changeEnd(change), oldPrevnewPrev);
      
oldPrev change.to;
      
newPrev to;
      if (
hint == "around") {
        var 
range doc.sel.ranges[i], inv cmp(range.headrange.anchor) < 0;
        
out[i] = new Range(inv to frominv from to);
      } else {
        
out[i] = new Range(fromfrom);
      }
    }
    return new 
Selection(outdoc.sel.primIndex);
  }

  
// Allow "beforeChange" event handlers to influence a change
  
function filterChange(docchangeupdate) {
    var 
obj = {
      
canceledfalse,
      
fromchange.from,
      
tochange.to,
      
textchange.text,
      
originchange.origin,
      
cancel: function() { this.canceled true; }
    };
    if (
updateobj.update = function(fromtotextorigin) {
      if (
fromthis.from clipPos(docfrom);
      if (
tothis.to clipPos(docto);
      if (
textthis.text text;
      if (
origin !== undefinedthis.origin origin;
    };
    
signal(doc"beforeChange"docobj);
    if (
doc.cmsignal(doc.cm"beforeChange"doc.cmobj);

    if (
obj.canceled) return null;
    return {
fromobj.fromtoobj.totextobj.textoriginobj.origin};
  }

  
// Apply a change to a document, and add it to the document's
  // history, and propagating it to all linked documents.
  
function makeChange(docchangeignoreReadOnly) {
    if (
doc.cm) {
      if (!
doc.cm.curOp) return operation(doc.cmmakeChange)(docchangeignoreReadOnly);
      if (
doc.cm.state.suppressEdits) return;
    }

    if (
hasHandler(doc"beforeChange") || doc.cm && hasHandler(doc.cm"beforeChange")) {
      
change filterChange(docchangetrue);
      if (!
change) return;
    }

    
// Possibly split or suppress the update based on the presence
    // of read-only spans in its range.
    
var split sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(docchange.fromchange.to);
    if (
split) {
      for (var 
split.length 1>= 0; --i)
        
makeChangeInner(doc, {fromsplit[i].fromtosplit[i].totext? [""] : change.text});
    } else {
      
makeChangeInner(docchange);
    }
  }

  function 
makeChangeInner(docchange) {
    if (
change.text.length == && change.text[0] == "" && cmp(change.fromchange.to) == 0) return;
    var 
selAfter computeSelAfterChange(docchange);
    
addChangeToHistory(docchangeselAfterdoc.cm doc.cm.curOp.id NaN);

    
makeChangeSingleDoc(docchangeselAfterstretchSpansOverChange(docchange));
    var 
rebased = [];

    
linkedDocs(doc, function(docsharedHist) {
      if (!
sharedHist && indexOf(rebaseddoc.history) == -1) {
        
rebaseHist(doc.historychange);
        
rebased.push(doc.history);
      }
      
makeChangeSingleDoc(docchangenullstretchSpansOverChange(docchange));
    });
  }

  
// Revert a change stored in a document's history.
  
function makeChangeFromHistory(doctypeallowSelectionOnly) {
    if (
doc.cm && doc.cm.state.suppressEdits) return;

    var 
hist doc.historyeventselAfter doc.sel;
    var 
source type == "undo" hist.done hist.undonedest type == "undo" hist.undone hist.done;

    
// Verify that there is a useable event (so that ctrl-z won't
    // needlessly clear selection events)
    
for (var 0source.lengthi++) {
      
event source[i];
      if (
allowSelectionOnly event.ranges && !event.equals(doc.sel) : !event.ranges)
        break;
    }
    if (
== source.length) return;
    
hist.lastOrigin hist.lastSelOrigin null;

    for (;;) {
      
event source.pop();
      if (
event.ranges) {
        
pushSelectionToHistory(eventdest);
        if (
allowSelectionOnly && !event.equals(doc.sel)) {
          
setSelection(docevent, {clearRedofalse});
          return;
        }
        
selAfter event;
      }
      else break;
    }

    
// Build up a reverse change object to add to the opposite history
    // stack (redo when undoing, and vice versa).
    
var antiChanges = [];
    
pushSelectionToHistory(selAfterdest);
    
dest.push({changesantiChangesgenerationhist.generation});
    
hist.generation event.generation || ++hist.maxGeneration;

    var 
filter hasHandler(doc"beforeChange") || doc.cm && hasHandler(doc.cm"beforeChange");

    for (var 
event.changes.length 1>= 0; --i) {
      var 
change event.changes[i];
      
change.origin type;
      if (
filter && !filterChange(docchangefalse)) {
        
source.length 0;
        return;
      }

      
antiChanges.push(historyChangeFromChange(docchange));

      var 
after computeSelAfterChange(docchangenull) : lst(source);
      
makeChangeSingleDoc(docchangeaftermergeOldSpans(docchange));
      if (!
&& doc.cmdoc.cm.scrollIntoView(change);
      var 
rebased = [];

      
// Propagate to the linked documents
      
linkedDocs(doc, function(docsharedHist) {
        if (!
sharedHist && indexOf(rebaseddoc.history) == -1) {
          
rebaseHist(doc.historychange);
          
rebased.push(doc.history);
        }
        
makeChangeSingleDoc(docchangenullmergeOldSpans(docchange));
      });
    }
  }

  
// Sub-views need their line numbers shifted when text is added
  // above or below them in the parent document.
  
function shiftDoc(docdistance) {
    if (
distance == 0) return;
    
doc.first += distance;
    
doc.sel = new Selection(map(doc.sel.ranges, function(range) {
      return new 
Range(Pos(range.anchor.line distancerange.anchor.ch),
                       
Pos(range.head.line distancerange.head.ch));
    }), 
doc.sel.primIndex);
    if (
doc.cm) {
      
regChange(doc.cmdoc.firstdoc.first distancedistance);
      for (var 
doc.cm.displayd.viewFromd.viewTol++)
        
regLineChange(doc.cml"gutter");
    }
  }

  
// More lower-level change function, handling only a single document
  // (not linked ones).
  
function makeChangeSingleDoc(docchangeselAfterspans) {
    if (
doc.cm && !doc.cm.curOp)
      return 
operation(doc.cmmakeChangeSingleDoc)(docchangeselAfterspans);

    if (
change.to.line doc.first) {
      
shiftDoc(docchange.text.length - (change.to.line change.from.line));
      return;
    }
    if (
change.from.line doc.lastLine()) return;

    
// Clip the change to the size of this doc
    
if (change.from.line doc.first) {
      var 
shift change.text.length - (doc.first change.from.line);
      
shiftDoc(docshift);
      
change = {fromPos(doc.first0), toPos(change.to.line shiftchange.to.ch),
                
text: [lst(change.text)], originchange.origin};
    }
    var 
last doc.lastLine();
    if (
change.to.line last) {
      
change = {fromchange.fromtoPos(lastgetLine(doclast).text.length),
                
text: [change.text[0]], originchange.origin};
    }

    
change.removed getBetween(docchange.fromchange.to);

    if (!
selAfterselAfter computeSelAfterChange(docchangenull);
    if (
doc.cmmakeChangeSingleDocInEditor(doc.cmchangespans);
    else 
updateDoc(docchangespans);
    
setSelectionNoUndo(docselAftersel_dontScroll);
  }

  
// Handle the interaction of a change to a document with the editor
  // that this document is part of.
  
function makeChangeSingleDocInEditor(cmchangespans) {
    var 
doc cm.docdisplay cm.displayfrom change.fromto change.to;

    var 
recomputeMaxLength falsecheckWidthStart from.line;
    if (!
cm.options.lineWrapping) {
      
checkWidthStart lineNo(visualLine(getLine(docfrom.line)));
      
doc.iter(checkWidthStartto.line 1, function(line) {
        if (
line == display.maxLine) {
          
recomputeMaxLength true;
          return 
true;
        }
      });
    }

    if (
doc.sel.contains(change.fromchange.to) > -1)
      
signalCursorActivity(cm);

    
updateDoc(docchangespansestimateHeight(cm));

    if (!
cm.options.lineWrapping) {
      
doc.iter(checkWidthStartfrom.line change.text.length, function(line) {
        var 
len lineLength(line);
        if (
len display.maxLineLength) {
          
display.maxLine line;
          
display.maxLineLength len;
          
display.maxLineChanged true;
          
recomputeMaxLength false;
        }
      });
      if (
recomputeMaxLengthcm.curOp.updateMaxLine true;
    }

    
// Adjust frontier, schedule worker
    
doc.frontier Math.min(doc.frontierfrom.line);
    
startWorker(cm400);

    var 
lendiff change.text.length - (to.line from.line) - 1;
    
// Remember that these lines changed, for updating the display
    
if (from.line == to.line && change.text.length == && !isWholeLineUpdate(cm.docchange))
      
regLineChange(cmfrom.line"text");
    else
      
regChange(cmfrom.lineto.line 1lendiff);

    var 
changesHandler hasHandler(cm"changes"), changeHandler hasHandler(cm"change");
    if (
changeHandler || changesHandler) {
      var 
obj = {
        
fromfromtoto,
        
textchange.text,
        
removedchange.removed,
        
originchange.origin
      
};
      if (
changeHandlersignalLater(cm"change"cmobj);
      if (
changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
    }
    
cm.display.selForContextMenu null;
  }

  function 
replaceRange(doccodefromtoorigin) {
    if (!
toto from;
    if (
cmp(tofrom) < 0) { var tmp toto fromfrom tmp; }
    if (
typeof code == "string"code splitLines(code);
    
makeChange(doc, {fromfromtototextcodeoriginorigin});
  }

  
// SCROLLING THINGS INTO VIEW

  // If an editor sits on the top or bottom of the window, partially
  // scrolled out of view, this ensures that the cursor is visible.
  
function maybeScrollWindow(cmcoords) {
    var 
display cm.displaybox display.sizer.getBoundingClientRect(), doScroll null;
    if (
coords.top box.top 0doScroll true;
    else if (
coords.bottom box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll false;
    if (
doScroll != null && !phantom) {
      var 
scrollNode elt("div""u200b"null"position: absolute; top: " +
                           (
coords.top display.viewOffset paddingTop(cm.display)) + "px; height: " +
                           (
coords.bottom coords.top scrollerCutOff) + "px; left: " +
                           
coords.left "px; width: 2px;");
      
cm.display.lineSpace.appendChild(scrollNode);
      
scrollNode.scrollIntoView(doScroll);
      
cm.display.lineSpace.removeChild(scrollNode);
    }
  }

  
// Scroll a given position into view (immediately), verifying that
  // it actually became visible (as line heights are accurately
  // measured, the position of something may 'drift' during drawing).
  
function scrollPosIntoView(cmposendmargin) {
    if (
margin == nullmargin 0;
    for (;;) {
      var 
changed falsecoords cursorCoords(cmpos);
      var 
endCoords = !end || end == pos coords cursorCoords(cmend);
      var 
scrollPos calculateScrollPos(cmMath.min(coords.leftendCoords.left),
                                         
Math.min(coords.topendCoords.top) - margin,
                                         
Math.max(coords.leftendCoords.left),
                                         
Math.max(coords.bottomendCoords.bottom) + margin);
      var 
startTop cm.doc.scrollTopstartLeft cm.doc.scrollLeft;
      if (
scrollPos.scrollTop != null) {
        
setScrollTop(cmscrollPos.scrollTop);
        if (
Math.abs(cm.doc.scrollTop startTop) > 1changed true;
      }
      if (
scrollPos.scrollLeft != null) {
        
setScrollLeft(cmscrollPos.scrollLeft);
        if (
Math.abs(cm.doc.scrollLeft startLeft) > 1changed true;
      }
      if (!
changed) return coords;
    }
  }

  
// Scroll a given set of coordinates into view (immediately).
  
function scrollIntoView(cmx1y1x2y2) {
    var 
scrollPos calculateScrollPos(cmx1y1x2y2);
    if (
scrollPos.scrollTop != nullsetScrollTop(cmscrollPos.scrollTop);
    if (
scrollPos.scrollLeft != nullsetScrollLeft(cmscrollPos.scrollLeft);
  }

  
// Calculate a new scroll position needed to scroll the given
  // rectangle into view. Returns an object with scrollTop and
  // scrollLeft properties. When these are undefined, the
  // vertical/horizontal position does not need to be adjusted.
  
function calculateScrollPos(cmx1y1x2y2) {
    var 
display cm.displaysnapMargin textHeight(cm.display);
    if (
y1 0y1 0;
    var 
screentop cm.curOp && cm.curOp.scrollTop != null cm.curOp.scrollTop display.scroller.scrollTop;
    var 
screen display.scroller.clientHeight scrollerCutOffresult = {};
    var 
docBottom cm.doc.height paddingVert(display);
    var 
atTop y1 snapMarginatBottom y2 docBottom snapMargin;
    if (
y1 screentop) {
      
result.scrollTop atTop y1;
    } else if (
y2 screentop screen) {
      var 
newTop Math.min(y1, (atBottom docBottom y2) - screen);
      if (
newTop != screentopresult.scrollTop newTop;
    }

    var 
screenleft cm.curOp && cm.curOp.scrollLeft != null cm.curOp.scrollLeft display.scroller.scrollLeft;
    var 
screenw display.scroller.clientWidth scrollerCutOff;
    
x1 += display.gutters.offsetWidthx2 += display.gutters.offsetWidth;
    var 
gutterw display.gutters.offsetWidth;
    var 
atLeft x1 gutterw 10;
    if (
x1 screenleft gutterw || atLeft) {
      if (
atLeftx1 0;
      
result.scrollLeft Math.max(0x1 10 gutterw);
    } else if (
x2 screenw screenleft 3) {
      
result.scrollLeft x2 10 screenw;
    }
    return 
result;
  }

  
// Store a relative adjustment to the scroll position in the current
  // operation (to be applied when the operation finishes).
  
function addToScrollPos(cmlefttop) {
    if (
left != null || top != nullresolveScrollToPos(cm);
    if (
left != null)
      
cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null cm.doc.scrollLeft cm.curOp.scrollLeft) + left;
    if (
top != null)
      
cm.curOp.scrollTop = (cm.curOp.scrollTop == null cm.doc.scrollTop cm.curOp.scrollTop) + top;
  }

  
// Make sure that at the end of the operation the current cursor is
  // shown.
  
function ensureCursorVisible(cm) {
    
resolveScrollToPos(cm);
    var 
cur cm.getCursor(), from curto cur;
    if (!
cm.options.lineWrapping) {
      
from cur.ch Pos(cur.linecur.ch 1) : cur;
      
to Pos(cur.linecur.ch 1);
    }
    
cm.curOp.scrollToPos = {fromfromtotomargincm.options.cursorScrollMarginisCursortrue};
  }

  
// When an operation has its scrollToPos property set, and another
  // scroll action is applied before the end of the operation, this
  // 'simulates' scrolling that position into view in a cheap way, so
  // that the effect of intermediate scroll commands is not ignored.
  
function resolveScrollToPos(cm) {
    var 
range cm.curOp.scrollToPos;
    if (
range) {
      
cm.curOp.scrollToPos null;
      var 
from estimateCoords(cmrange.from), to estimateCoords(cmrange.to);
      var 
sPos calculateScrollPos(cmMath.min(from.leftto.left),
                                    
Math.min(from.topto.top) - range.margin,
                                    
Math.max(from.rightto.right),
                                    
Math.max(from.bottomto.bottom) + range.margin);
      
cm.scrollTo(sPos.scrollLeftsPos.scrollTop);
    }
  }

  
// API UTILITIES

  // Indent the given line. The how parameter can be "smart",
  // "add"/null, "subtract", or "prev". When aggressive is false
  // (typically set to true for forced single-line indents), empty
  // lines are not indented, and places where the mode returns Pass
  // are left alone.
  
function indentLine(cmnhowaggressive) {
    var 
doc cm.docstate;
    if (
how == nullhow "add";
    if (
how == "smart") {
      
// Fall back to "prev" when the mode doesn't have an indentation
      // method.
      
if (!cm.doc.mode.indenthow "prev";
      else 
state getStateBefore(cmn);
    }

    var 
tabSize cm.options.tabSize;
    var 
line getLine(docn), curSpace countColumn(line.textnulltabSize);
    if (
line.stateAfterline.stateAfter null;
    var 
curSpaceString line.text.match(/^s*/)[0], indentation;
    if (!
aggressive && !/S/.test(line.text)) {
      
indentation 0;
      
how "not";
    } else if (
how == "smart") {
      
indentation cm.doc.mode.indent(stateline.text.slice(curSpaceString.length), line.text);
      if (
indentation == Pass) {
        if (!
aggressive) return;
        
how "prev";
      }
    }
    if (
how == "prev") {
      if (
doc.firstindentation countColumn(getLine(docn-1).textnulltabSize);
      else 
indentation 0;
    } else if (
how == "add") {
      
indentation curSpace cm.options.indentUnit;
    } else if (
how == "subtract") {
      
indentation curSpace cm.options.indentUnit;
    } else if (
typeof how == "number") {
      
indentation curSpace how;
    }
    
indentation Math.max(0indentation);

    var 
indentString ""pos 0;
    if (
cm.options.indentWithTabs)
      for (var 
Math.floor(indentation tabSize); i; --i) {pos += tabSizeindentString += "t";}
    if (
pos indentationindentString += spaceStr(indentation pos);

    if (
indentString != curSpaceString) {
      
replaceRange(cm.docindentStringPos(n0), Pos(ncurSpaceString.length), "+input");
    } else {
      
// Ensure that, if the cursor was in the whitespace at the start
      // of the line, it is moved to the end of that space.
      
for (var 0doc.sel.ranges.lengthi++) {
        var 
range doc.sel.ranges[i];
        if (
range.head.line == && range.head.ch curSpaceString.length) {
          var 
pos Pos(ncurSpaceString.length);
          
replaceOneSelection(doci, new Range(pospos));
          break;
        }
      }
    }
    
line.stateAfter null;
  }

  
// Utility for applying a change to a line by handle or number,
  // returning the number and optionally registering the line as
  // changed.
  
function changeLine(cmhandlechangeTypeop) {
    var 
no handleline handledoc cm.doc;
    if (
typeof handle == "number"line getLine(docclipLine(dochandle));
    else 
no lineNo(handle);
    if (
no == null) return null;
    if (
op(lineno)) regLineChange(cmnochangeType);
    return 
line;
  }

  
// Helper for deleting text near the selection(s), used to implement
  // backspace, delete, and similar functionality.
  
function deleteNearSelection(cmcompute) {
    var 
ranges cm.doc.sel.rangeskill = [];
    
// Build up a set of ranges to kill first, merging overlapping
    // ranges.
    
for (var 0ranges.lengthi++) {
      var 
toKill compute(ranges[i]);
      while (
kill.length && cmp(toKill.fromlst(kill).to) <= 0) {
        var 
replaced kill.pop();
        if (
cmp(replaced.fromtoKill.from) < 0) {
          
toKill.from replaced.from;
          break;
        }
      }
      
kill.push(toKill);
    }
    
// Next, remove those actual ranges.
    
runInOp(cm, function() {
      for (var 
kill.length 1>= 0i--)
        
replaceRange(cm.doc""kill[i].fromkill[i].to"+delete");
      
ensureCursorVisible(cm);
    });
  }

  
// Used for horizontal relative motion. Dir is -1 or 1 (left or
  // right), unit can be "char", "column" (like char, but doesn't
  // cross line boundaries), "word" (across next word), or "group" (to
  // the start of next group of word or non-word-non-whitespace
  // chars). The visually param controls whether, in right-to-left
  // text, direction 1 means to move towards the next index in the
  // string, or towards the character to the right of the current
  // position. The resulting position will have a hitSide=true
  // property if it reached the end of the document.
  
function findPosH(docposdirunitvisually) {
    var 
line pos.linech pos.chorigDir dir;
    var 
lineObj getLine(docline);
    var 
possible true;
    function 
findNextLine() {
      var 
line dir;
      if (
doc.first || >= doc.first doc.size) return (possible false);
      
line l;
      return 
lineObj getLine(docl);
    }
    function 
moveOnce(boundToLine) {
      var 
next = (visually moveVisually moveLogically)(lineObjchdirtrue);
      if (
next == null) {
        if (!
boundToLine && findNextLine()) {
          if (
visuallych = (dir lineRight lineLeft)(lineObj);
          else 
ch dir lineObj.text.length 0;
        } else return (
possible false);
      } else 
ch next;
      return 
true;
    }

    if (
unit == "char"moveOnce();
    else if (
unit == "column"moveOnce(true);
    else if (
unit == "word" || unit == "group") {
      var 
sawType nullgroup unit == "group";
      var 
helper doc.cm && doc.cm.getHelper(pos"wordChars");
      for (var 
first true;; first false) {
        if (
dir && !moveOnce(!first)) break;
        var 
cur lineObj.text.charAt(ch) || "n";
        var 
type isWordChar(curhelper) ? "w"
          
group && cur == "n" "n"
          
: !group || /s/.test(cur) ? null
          
"p";
        if (
group && !first && !typetype "s";
        if (
sawType && sawType != type) {
          if (
dir 0) {dir 1moveOnce();}
          break;
        }

        if (
typesawType type;
        if (
dir && !moveOnce(!first)) break;
      }
    }
    var 
result skipAtomic(docPos(linech), origDirtrue);
    if (!
possibleresult.hitSide true;
    return 
result;
  }

  
// For relative vertical movement. Dir may be -1 or 1. Unit can be
  // "page" or "line". The resulting position will have a hitSide=true
  // property if it reached the end of the document.
  
function findPosV(cmposdirunit) {
    var 
doc cm.docpos.lefty;
    if (
unit == "page") {
      var 
pageSize Math.min(cm.display.wrapper.clientHeightwindow.innerHeight || document.documentElement.clientHeight);
      
pos.top dir * (pageSize - (dir 1.5 .5) * textHeight(cm.display));
    } else if (
unit == "line") {
      
dir pos.bottom pos.top 3;
    }
    for (;;) {
      var 
target coordsChar(cmxy);
      if (!
target.outside) break;
      if (
dir <= >= doc.height) { target.hitSide true; break; }
      
+= dir 5;
    }
    return 
target;
  }

  
// Find the word at the given position (as returned by coordsChar).
  
function findWordAt(cmpos) {
    var 
doc cm.docline getLine(docpos.line).text;
    var 
start pos.chend pos.ch;
    if (
line) {
      var 
helper cm.getHelper(pos"wordChars");
      if ((
pos.xRel || end == line.length) && start) --start; else ++end;
      var 
startChar line.charAt(start);
      var 
check isWordChar(startCharhelper)
        ? function(
ch) { return isWordChar(chhelper); }
        : /
s/.test(startChar) ? function(ch) {return /s/.test(ch);}
        : function(
ch) {return !/s/.test(ch) && !isWordChar(ch);};
      while (
start && check(line.charAt(start 1))) --start;
      while (
end line.length && check(line.charAt(end))) ++end;
    }
    return new 
Range(Pos(pos.linestart), Pos(pos.lineend));
  }

  
// EDITOR METHODS

  // The publicly visible API. Note that methodOp(f) means
  // 'wrap f in an operation, performed on its `this` parameter'.

  // This is not the complete set of editor methods. Most of the
  // methods defined on the Doc type are also injected into
  // CodeMirror.prototype, for backwards compatibility and
  // convenience.

  
CodeMirror.prototype = {
    
constructorCodeMirror,
    
focus: function(){window.focus(); focusInput(this); fastPoll(this);},

    
setOption: function(optionvalue) {
      var 
options this.optionsold options[option];
      if (
options[option] == value && option != "mode") return;
      
options[option] = value;
      if (
optionHandlers.hasOwnProperty(option))
        
operation(thisoptionHandlers[option])(thisvalueold);
    },

    
getOption: function(option) {return this.options[option];},
    
getDoc: function() {return this.doc;},

    
addKeyMap: function(mapbottom) {
      
this.state.keyMaps[bottom "push" "unshift"](map);
    },
    
removeKeyMap: function(map) {
      var 
maps this.state.keyMaps;
      for (var 
0maps.length; ++i)
        if (
maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
          
maps.splice(i1);
          return 
true;
        }
    },

    
addOverlaymethodOp(function(specoptions) {
      var 
mode spec.token spec CodeMirror.getMode(this.optionsspec);
      if (
mode.startState) throw new Error("Overlays may not be stateful.");
      
this.state.overlays.push({modemodemodeSpecspecopaqueoptions && options.opaque});
      
this.state.modeGen++;
      
regChange(this);
    }),
    
removeOverlaymethodOp(function(spec) {
      var 
overlays this.state.overlays;
      for (var 
0overlays.length; ++i) {
        var 
cur overlays[i].modeSpec;
        if (
cur == spec || typeof spec == "string" && cur.name == spec) {
          
overlays.splice(i1);
          
this.state.modeGen++;
          
regChange(this);
          return;
        }
      }
    }),

    
indentLinemethodOp(function(ndiraggressive) {
      if (
typeof dir != "string" && typeof dir != "number") {
        if (
dir == nulldir this.options.smartIndent "smart" "prev";
        else 
dir dir "add" "subtract";
      }
      if (
isLine(this.docn)) indentLine(thisndiraggressive);
    }),
    
indentSelectionmethodOp(function(how) {
      var 
ranges this.doc.sel.rangesend = -1;
      for (var 
0ranges.lengthi++) {
        var 
range ranges[i];
        if (!
range.empty()) {
          var 
start Math.max(endrange.from().line);
          var 
to range.to();
          
end Math.min(this.lastLine(), to.line - (to.ch 1)) + 1;
          for (var 
startend; ++j)
            
indentLine(thisjhow);
        } else if (
range.head.line end) {
          
indentLine(thisrange.head.linehowtrue);
          
end range.head.line;
          if (
== this.doc.sel.primIndexensureCursorVisible(this);
        }
      }
    }),

    
// Fetch the parser token for a given character. Useful for hacks
    // that want to inspect the mode state (say, for completion).
    
getTokenAt: function(posprecise) {
      var 
doc this.doc;
      
pos clipPos(docpos);
      var 
state getStateBefore(thispos.lineprecise), mode this.doc.mode;
      var 
line getLine(docpos.line);
      var 
stream = new StringStream(line.textthis.options.tabSize);
      while (
stream.pos pos.ch && !stream.eol()) {
        
stream.start stream.pos;
        var 
style readToken(modestreamstate);
      }
      return {
startstream.start,
              
endstream.pos,
              
stringstream.current(),
              
typestyle || null,
              
statestate};
    },

    
getTokenTypeAt: function(pos) {
      
pos clipPos(this.docpos);
      var 
styles getLineStyles(thisgetLine(this.docpos.line));
      var 
before 0after = (styles.length 1) / 2ch pos.ch;
      var 
type;
      if (
ch == 0type styles[2];
      else for (;;) {
        var 
mid = (before after) >> 1;
        if ((
mid styles[mid 1] : 0) >= chafter mid;
        else if (
styles[mid 1] < chbefore mid 1;
        else { 
type styles[mid 2]; break; }
      }
      var 
cut type type.indexOf("cm-overlay ") : -1;
      return 
cut type cut == null type.slice(0cut 1);
    },

    
getModeAt: function(pos) {
      var 
mode this.doc.mode;
      if (!
mode.innerMode) return mode;
      return 
CodeMirror.innerMode(modethis.getTokenAt(pos).state).mode;
    },

    
getHelper: function(postype) {
      return 
this.getHelpers(postype)[0];
    },

    
getHelpers: function(postype) {
      var 
found = [];
      if (!
helpers.hasOwnProperty(type)) return helpers;
      var 
help helpers[type], mode this.getModeAt(pos);
      if (
typeof mode[type] == "string") {
        if (
help[mode[type]]) found.push(help[mode[type]]);
      } else if (
mode[type]) {
        for (var 
0mode[type].lengthi++) {
          var 
val help[mode[type][i]];
          if (
valfound.push(val);
        }
      } else if (
mode.helperType && help[mode.helperType]) {
        
found.push(help[mode.helperType]);
      } else if (
help[mode.name]) {
        
found.push(help[mode.name]);
      }
      for (var 
0help._global.lengthi++) {
        var 
cur help._global[i];
        if (
cur.pred(modethis) && indexOf(foundcur.val) == -1)
          
found.push(cur.val);
      }
      return 
found;
    },

    
getStateAfter: function(lineprecise) {
      var 
doc this.doc;
      
line clipLine(docline == null doc.first doc.size 1line);
      return 
getStateBefore(thisline 1precise);
    },

    
cursorCoords: function(startmode) {
      var 
posrange this.doc.sel.primary();
      if (
start == nullpos range.head;
      else if (
typeof start == "object"pos clipPos(this.docstart);
      else 
pos start range.from() : range.to();
      return 
cursorCoords(thisposmode || "page");
    },

    
charCoords: function(posmode) {
      return 
charCoords(thisclipPos(this.docpos), mode || "page");
    },

    
coordsChar: function(coordsmode) {
      
coords fromCoordSystem(thiscoordsmode || "page");
      return 
coordsChar(thiscoords.leftcoords.top);
    },

    
lineAtHeight: function(heightmode) {
      
height fromCoordSystem(this, {topheightleft0}, mode || "page").top;
      return 
lineAtHeight(this.docheight this.display.viewOffset);
    },
    
heightAtLine: function(linemode) {
      var 
end falselast this.doc.first this.doc.size 1;
      if (
line this.doc.firstline this.doc.first;
      else if (
line last) { line lastend true; }
      var 
lineObj getLine(this.docline);
      return 
intoCoordSystem(thislineObj, {top0left0}, mode || "page").top +
        (
end this.doc.height heightAtLine(lineObj) : 0);
    },

    
defaultTextHeight: function() { return textHeight(this.display); },
    
defaultCharWidth: function() { return charWidth(this.display); },

    
setGutterMarkermethodOp(function(linegutterIDvalue) {
      return 
changeLine(thisline"gutter", function(line) {
        var 
markers line.gutterMarkers || (line.gutterMarkers = {});
        
markers[gutterID] = value;
        if (!
value && isEmpty(markers)) line.gutterMarkers null;
        return 
true;
      });
    }),

    
clearGuttermethodOp(function(gutterID) {
      var 
cm thisdoc cm.docdoc.first;
      
doc.iter(function(line) {
        if (
line.gutterMarkers && line.gutterMarkers[gutterID]) {
          
line.gutterMarkers[gutterID] = null;
          
regLineChange(cmi"gutter");
          if (
isEmpty(line.gutterMarkers)) line.gutterMarkers null;
        }
        ++
i;
      });
    }),

    
addLineClassmethodOp(function(handlewherecls) {
      return 
changeLine(thishandle"class", function(line) {
        var 
prop where == "text" "textClass" where == "background" "bgClass" "wrapClass";
        if (!
line[prop]) line[prop] = cls;
        else if (new 
RegExp("(?:^|\s)" cls "(?:$|\s)").test(line[prop])) return false;
        else 
line[prop] += " " cls;
        return 
true;
      });
    }),

    
removeLineClassmethodOp(function(handlewherecls) {
      return 
changeLine(thishandle"class", function(line) {
        var 
prop where == "text" "textClass" where == "background" "bgClass" "wrapClass";
        var 
cur line[prop];
        if (!
cur) return false;
        else if (
cls == nullline[prop] = null;
        else {
          var 
found cur.match(new RegExp("(?:^|\s+)" cls "(?:$|\s+)"));
          if (!
found) return false;
          var 
end found.index found[0].length;
          
line[prop] = cur.slice(0found.index) + (!found.index || end == cur.length "" " ") + cur.slice(end) || null;
        }
        return 
true;
      });
    }),

    
addLineWidgetmethodOp(function(handlenodeoptions) {
      return 
addLineWidget(thishandlenodeoptions);
    }),

    
removeLineWidget: function(widget) { widget.clear(); },

    
lineInfo: function(line) {
      if (
typeof line == "number") {
        if (!
isLine(this.docline)) return null;
        var 
line;
        
line getLine(this.docline);
        if (!
line) return null;
      } else {
        var 
lineNo(line);
        if (
== null) return null;
      }
      return {
linenhandlelinetextline.textgutterMarkersline.gutterMarkers,
              
textClassline.textClassbgClassline.bgClasswrapClassline.wrapClass,
              
widgetsline.widgets};
    },

    
getViewport: function() { return {fromthis.display.viewFromtothis.display.viewTo};},

    
addWidget: function(posnodescrollverthoriz) {
      var 
display this.display;
      
pos cursorCoords(thisclipPos(this.docpos));
      var 
top pos.bottomleft pos.left;
      
node.style.position "absolute";
      
display.sizer.appendChild(node);
      if (
vert == "over") {
        
top pos.top;
      } else if (
vert == "above" || vert == "near") {
        var 
vspace Math.max(display.wrapper.clientHeightthis.doc.height),
        
hspace Math.max(display.sizer.clientWidthdisplay.lineSpace.clientWidth);
        
// Default to positioning above (if specified and possible); otherwise default to positioning below
        
if ((vert == 'above' || pos.bottom node.offsetHeight vspace) && pos.top node.offsetHeight)
          
top pos.top node.offsetHeight;
        else if (
pos.bottom node.offsetHeight <= vspace)
          
top pos.bottom;
        if (
left node.offsetWidth hspace)
          
left hspace node.offsetWidth;
      }
      
node.style.top top "px";
      
node.style.left node.style.right "";
      if (
horiz == "right") {
        
left display.sizer.clientWidth node.offsetWidth;
        
node.style.right "0px";
      } else {
        if (
horiz == "left"left 0;
        else if (
horiz == "middle"left = (display.sizer.clientWidth node.offsetWidth) / 2;
        
node.style.left left "px";
      }
      if (
scroll)
        
scrollIntoView(thislefttopleft node.offsetWidthtop node.offsetHeight);
    },

    
triggerOnKeyDownmethodOp(onKeyDown),
    
triggerOnKeyPressmethodOp(onKeyPress),
    
triggerOnKeyUpmethodOp(onKeyUp),

    
execCommand: function(cmd) {
      if (
commands.hasOwnProperty(cmd))
        return 
commands[cmd](this);
    },

    
findPosH: function(fromamountunitvisually) {
      var 
dir 1;
      if (
amount 0) { dir = -1amount = -amount; }
      for (var 
0cur clipPos(this.docfrom); amount; ++i) {
        
cur findPosH(this.doccurdirunitvisually);
        if (
cur.hitSide) break;
      }
      return 
cur;
    },

    
moveHmethodOp(function(dirunit) {
      var 
cm this;
      
cm.extendSelectionsBy(function(range) {
        if (
cm.display.shift || cm.doc.extend || range.empty())
          return 
findPosH(cm.docrange.headdirunitcm.options.rtlMoveVisually);
        else
          return 
dir range.from() : range.to();
      }, 
sel_move);
    }),

    
deleteHmethodOp(function(dirunit) {
      var 
sel this.doc.seldoc this.doc;
      if (
sel.somethingSelected())
        
doc.replaceSelection(""null"+delete");
      else
        
deleteNearSelection(this, function(range) {
          var 
other findPosH(docrange.headdirunitfalse);
          return 
dir ? {fromothertorange.head} : {fromrange.headtoother};
        });
    }),

    
findPosV: function(fromamountunitgoalColumn) {
      var 
dir 1goalColumn;
      if (
amount 0) { dir = -1amount = -amount; }
      for (var 
0cur clipPos(this.docfrom); amount; ++i) {
        var 
coords cursorCoords(thiscur"div");
        if (
== nullcoords.left;
        else 
coords.left x;
        
cur findPosV(thiscoordsdirunit);
        if (
cur.hitSide) break;
      }
      return 
cur;
    },

    
moveVmethodOp(function(dirunit) {
      var 
cm thisdoc this.docgoals = [];
      var 
collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
      
doc.extendSelectionsBy(function(range) {
        if (
collapse)
          return 
dir range.from() : range.to();
        var 
headPos cursorCoords(cmrange.head"div");
        if (
range.goalColumn != nullheadPos.left range.goalColumn;
        
goals.push(headPos.left);
        var 
pos findPosV(cmheadPosdirunit);
        if (
unit == "page" && range == doc.sel.primary())
          
addToScrollPos(cmnullcharCoords(cmpos"div").top headPos.top);
        return 
pos;
      }, 
sel_move);
      if (
goals.length) for (var 0doc.sel.ranges.lengthi++)
        
doc.sel.ranges[i].goalColumn goals[i];
    }),

    
toggleOverwrite: function(value) {
      if (
value != null && value == this.state.overwrite) return;
      if (
this.state.overwrite = !this.state.overwrite)
        
addClass(this.display.cursorDiv"CodeMirror-overwrite");
      else
        
rmClass(this.display.cursorDiv"CodeMirror-overwrite");

      
signal(this"overwriteToggle"thisthis.state.overwrite);
    },
    
hasFocus: function() { return activeElt() == this.display.input; },

    
scrollTomethodOp(function(xy) {
      if (
!= null || != nullresolveScrollToPos(this);
      if (
!= nullthis.curOp.scrollLeft x;
      if (
!= nullthis.curOp.scrollTop y;
    }),
    
getScrollInfo: function() {
      var 
scroller this.display.scrollerco scrollerCutOff;
      return {
leftscroller.scrollLefttopscroller.scrollTop,
              
heightscroller.scrollHeight cowidthscroller.scrollWidth co,
              
clientHeightscroller.clientHeight coclientWidthscroller.clientWidth co};
    },

    
scrollIntoViewmethodOp(function(rangemargin) {
      if (
range == null) {
        
range = {fromthis.doc.sel.primary().headtonull};
        if (
margin == nullmargin this.options.cursorScrollMargin;
      } else if (
typeof range == "number") {
        
range = {fromPos(range0), tonull};
      } else if (
range.from == null) {
        
range = {fromrangetonull};
      }
      if (!
range.torange.to range.from;
      
range.margin margin || 0;

      if (
range.from.line != null) {
        
resolveScrollToPos(this);
        
this.curOp.scrollToPos range;
      } else {
        var 
sPos calculateScrollPos(thisMath.min(range.from.leftrange.to.left),
                                      
Math.min(range.from.toprange.to.top) - range.margin,
                                      
Math.max(range.from.rightrange.to.right),
                                      
Math.max(range.from.bottomrange.to.bottom) + range.margin);
        
this.scrollTo(sPos.scrollLeftsPos.scrollTop);
      }
    }),

    
setSizemethodOp(function(widthheight) {
      function 
interpret(val) {
        return 
typeof val == "number" || /^d+$/.test(String(val)) ? val "px" val;
      }
      if (
width != nullthis.display.wrapper.style.width interpret(width);
      if (
height != nullthis.display.wrapper.style.height interpret(height);
      if (
this.options.lineWrappingclearLineMeasurementCache(this);
      
this.curOp.forceUpdate true;
      
signal(this"refresh"this);
    }),

    
operation: function(f){return runInOp(thisf);},

    
refreshmethodOp(function() {
      var 
oldHeight this.display.cachedTextHeight;
      
regChange(this);
      
this.curOp.forceUpdate true;
      
clearCaches(this);
      
this.scrollTo(this.doc.scrollLeftthis.doc.scrollTop);
      
updateGutterSpace(this);
      if (
oldHeight == null || Math.abs(oldHeight textHeight(this.display)) > .5)
        
estimateLineHeights(this);
      
signal(this"refresh"this);
    }),

    
swapDocmethodOp(function(doc) {
      var 
old this.doc;
      
old.cm null;
      
attachDoc(thisdoc);
      
clearCaches(this);
      
resetInput(this);
      
this.scrollTo(doc.scrollLeftdoc.scrollTop);
      
signalLater(this"swapDoc"thisold);
      return 
old;
    }),

    
getInputField: function(){return this.display.input;},
    
getWrapperElement: function(){return this.display.wrapper;},
    
getScrollerElement: function(){return this.display.scroller;},
    
getGutterElement: function(){return this.display.gutters;}
  };
  
eventMixin(CodeMirror);

  
// OPTION DEFAULTS

  // The default configuration options.
  
var defaults CodeMirror.defaults = {};
  
// Functions to run when options are changed.
  
var optionHandlers CodeMirror.optionHandlers = {};

  function 
option(namedeflthandlenotOnInit) {
    
CodeMirror.defaults[name] = deflt;
    if (
handleoptionHandlers[name] =
      
notOnInit ? function(cmvalold) {if (old != Inithandle(cmvalold);} : handle;
  }

  
// Passed to option handlers when there is no old value.
  
var Init CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};

  
// These two are, on init, called from the constructor because they
  // have to be initialized before the editor can start at all.
  
option("value""", function(cmval) {
    
cm.setValue(val);
  }, 
true);
  
option("mode"null, function(cmval) {
    
cm.doc.modeOption val;
    
loadMode(cm);
  }, 
true);

  
option("indentUnit"2loadModetrue);
  
option("indentWithTabs"false);
  
option("smartIndent"true);
  
option("tabSize"4, function(cm) {
    
resetModeState(cm);
    
clearCaches(cm);
    
regChange(cm);
  }, 
true);
  
option("specialChars", /[tu0000-u0019u00adu200bu2028u2029ufeff]/g, function(cmval) {
    
cm.options.specialChars = new RegExp(val.source + (val.test("t") ? "" "|t"), "g");
    
cm.refresh();
  }, 
true);
  
option("specialCharPlaceholder"defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
  
option("electricChars"true);
  
option("rtlMoveVisually", !windows);
  
option("wholeLineUpdateBefore"true);

  
option("theme""default", function(cm) {
    
themeChanged(cm);
    
guttersChanged(cm);
  }, 
true);
  
option("keyMap""default"keyMapChanged);
  
option("extraKeys"null);

  
option("lineWrapping"falsewrappingChangedtrue);
  
option("gutters", [], function(cm) {
    
setGuttersForLineNumbers(cm.options);
    
guttersChanged(cm);
  }, 
true);
  
option("fixedGutter"true, function(cmval) {
    
cm.display.gutters.style.left val compensateForHScroll(cm.display) + "px" "0";
    
cm.refresh();
  }, 
true);
  
option("coverGutterNextToScrollbar"falseupdateScrollbarstrue);
  
option("lineNumbers"false, function(cm) {
    
setGuttersForLineNumbers(cm.options);
    
guttersChanged(cm);
  }, 
true);
  
option("firstLineNumber"1guttersChangedtrue);
  
option("lineNumberFormatter", function(integer) {return integer;}, guttersChangedtrue);
  
option("showCursorWhenSelecting"falseupdateSelectiontrue);

  
option("resetSelectionOnContextMenu"true);

  
option("readOnly"false, function(cmval) {
    if (
val == "nocursor") {
      
onBlur(cm);
      
cm.display.input.blur();
      
cm.display.disabled true;
    } else {
      
cm.display.disabled false;
      if (!
valresetInput(cm);
    }
  });
  
option("disableInput"false, function(cmval) {if (!valresetInput(cm);}, true);
  
option("dragDrop"true);

  
option("cursorBlinkRate"530);
  
option("cursorScrollMargin"0);
  
option("cursorHeight"1);
  
option("workTime"100);
  
option("workDelay"100);
  
option("flattenSpans"trueresetModeStatetrue);
  
option("addModeClass"falseresetModeStatetrue);
  
option("pollInterval"100);
  
option("undoDepth"200, function(cmval){cm.doc.history.undoDepth val;});
  
option("historyEventDelay"1250);
  
option("viewportMargin"10, function(cm){cm.refresh();}, true);
  
option("maxHighlightLength"10000resetModeStatetrue);
  
option("moveInputWithCursor"true, function(cmval) {
    if (!
valcm.display.inputDiv.style.top cm.display.inputDiv.style.left 0;
  });

  
option("tabindex"null, function(cmval) {
    
cm.display.input.tabIndex val || "";
  });
  
option("autofocus"null);

  
// MODE DEFINITION AND QUERYING

  // Known modes, by name and by MIME
  
var modes CodeMirror.modes = {}, mimeModes CodeMirror.mimeModes = {};

  
// Extra arguments are stored as the mode's dependencies, which is
  // used by (legacy) mechanisms like loadmode.js to automatically
  // load a mode. (Preferred mechanism is the require/define calls.)
  
CodeMirror.defineMode = function(namemode) {
    if (!
CodeMirror.defaults.mode && name != "null"CodeMirror.defaults.mode name;
    if (
arguments.length 2) {
      
mode.dependencies = [];
      for (var 
2arguments.length; ++imode.dependencies.push(arguments[i]);
    }
    
modes[name] = mode;
  };

  
CodeMirror.defineMIME = function(mimespec) {
    
mimeModes[mime] = spec;
  };

  
// Given a MIME type, a {name, ...options} config object, or a name
  // string, return a mode config object.
  
CodeMirror.resolveMode = function(spec) {
    if (
typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
      
spec mimeModes[spec];
    } else if (
spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
      var 
found mimeModes[spec.name];
      if (
typeof found == "string"found = {namefound};
      
spec createObj(foundspec);
      
spec.name found.name;
    } else if (
typeof spec == "string" && /^[w-]+/[w-]++xml$/.test(spec)) {
      return 
CodeMirror.resolveMode("application/xml");
    }
    if (
typeof spec == "string") return {namespec};
    else return 
spec || {name"null"};
  };

  
// Given a mode spec (anything that resolveMode accepts), find and
  // initialize an actual mode object.
  
CodeMirror.getMode = function(optionsspec) {
    var 
spec CodeMirror.resolveMode(spec);
    var 
mfactory modes[spec.name];
    if (!
mfactory) return CodeMirror.getMode(options"text/plain");
    var 
modeObj mfactory(optionsspec);
    if (
modeExtensions.hasOwnProperty(spec.name)) {
      var 
exts modeExtensions[spec.name];
      for (var 
prop in exts) {
        if (!
exts.hasOwnProperty(prop)) continue;
        if (
modeObj.hasOwnProperty(prop)) modeObj["_" prop] = modeObj[prop];
        
modeObj[prop] = exts[prop];
      }
    }
    
modeObj.name spec.name;
    if (
spec.helperTypemodeObj.helperType spec.helperType;
    if (
spec.modeProps) for (var prop in spec.modeProps)
      
modeObj[prop] = spec.modeProps[prop];

    return 
modeObj;
  };

  
// Minimal default mode.
  
CodeMirror.defineMode("null", function() {
    return {
token: function(stream) {stream.skipToEnd();}};
  });
  
CodeMirror.defineMIME("text/plain""null");

  
// This can be used to attach properties to mode objects from
  // outside the actual mode definition.
  
var modeExtensions CodeMirror.modeExtensions = {};
  
CodeMirror.extendMode = function(modeproperties) {
    var 
exts modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
    
copyObj(propertiesexts);
  };

  
// EXTENSIONS

  
CodeMirror.defineExtension = function(namefunc) {
    
CodeMirror.prototype[name] = func;
  };
  
CodeMirror.defineDocExtension = function(namefunc) {
    
Doc.prototype[name] = func;
  };
  
CodeMirror.defineOption option;

  var 
initHooks = [];
  
CodeMirror.defineInitHook = function(f) {initHooks.push(f);};

  var 
helpers CodeMirror.helpers = {};
  
CodeMirror.registerHelper = function(typenamevalue) {
    if (!
helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
    
helpers[type][name] = value;
  };
  
CodeMirror.registerGlobalHelper = function(typenamepredicatevalue) {
    
CodeMirror.registerHelper(typenamevalue);
    
helpers[type]._global.push({predpredicatevalvalue});
  };

  
// MODE STATE HANDLING

  // Utility functions for working with state. Exported because nested
  // modes need to do this for their inner modes.

  
var copyState CodeMirror.copyState = function(modestate) {
    if (
state === true) return state;
    if (
mode.copyState) return mode.copyState(state);
    var 
nstate = {};
    for (var 
n in state) {
      var 
val state[n];
      if (
val instanceof Array) val val.concat([]);
      
nstate[n] = val;
    }
    return 
nstate;
  };

  var 
startState CodeMirror.startState = function(modea1a2) {
    return 
mode.startState mode.startState(a1a2) : true;
  };

  
// Given a mode and a state (for that mode), find the inner mode and
  // state at the position that the state refers to.
  
CodeMirror.innerMode = function(modestate) {
    while (
mode.innerMode) {
      var 
info mode.innerMode(state);
      if (!
info || info.mode == mode) break;
      
state info.state;
      
mode info.mode;
    }
    return 
info || {modemodestatestate};
  };

  
// STANDARD COMMANDS

  // Commands are parameter-less actions that can be performed on an
  // editor, mostly used for keybindings.
  
var commands CodeMirror.commands = {
    
selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
    
singleSelection: function(cm) {
      
cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
    },
    
killLine: function(cm) {
      
deleteNearSelection(cm, function(range) {
        if (
range.empty()) {
          var 
len getLine(cm.docrange.head.line).text.length;
          if (
range.head.ch == len && range.head.line cm.lastLine())
            return {
fromrange.headtoPos(range.head.line 10)};
          else
            return {
fromrange.headtoPos(range.head.linelen)};
        } else {
          return {
fromrange.from(), torange.to()};
        }
      });
    },
    
deleteLine: function(cm) {
      
deleteNearSelection(cm, function(range) {
        return {
fromPos(range.from().line0),
                
toclipPos(cm.docPos(range.to().line 10))};
      });
    },
    
delLineLeft: function(cm) {
      
deleteNearSelection(cm, function(range) {
        return {
fromPos(range.from().line0), torange.from()};
      });
    },
    
undo: function(cm) {cm.undo();},
    
redo: function(cm) {cm.redo();},
    
undoSelection: function(cm) {cm.undoSelection();},
    
redoSelection: function(cm) {cm.redoSelection();},
    
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
    
goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
    
goLineStart: function(cm) {
      
cm.extendSelectionsBy(function(range) { return lineStart(cmrange.head.line); }, sel_move);
    },
    
goLineStartSmart: function(cm) {
      
cm.extendSelectionsBy(function(range) {
        var 
start lineStart(cmrange.head.line);
        var 
line cm.getLineHandle(start.line);
        var 
order getOrder(line);
        if (!
order || order[0].level == 0) {
          var 
firstNonWS Math.max(0line.text.search(/S/));
          var 
inWS range.head.line == start.line && range.head.ch <= firstNonWS && range.head.ch;
          return 
Pos(start.lineinWS firstNonWS);
        }
        return 
start;
      }, 
sel_move);
    },
    
goLineEnd: function(cm) {
      
cm.extendSelectionsBy(function(range) { return lineEnd(cmrange.head.line); }, sel_move);
    },
    
goLineRight: function(cm) {
      
cm.extendSelectionsBy(function(range) {
        var 
top cm.charCoords(range.head"div").top 5;
        return 
cm.coordsChar({leftcm.display.lineDiv.offsetWidth 100toptop}, "div");
      }, 
sel_move);
    },
    
goLineLeft: function(cm) {
      
cm.extendSelectionsBy(function(range) {
        var 
top cm.charCoords(range.head"div").top 5;
        return 
cm.coordsChar({left0toptop}, "div");
      }, 
sel_move);
    },
    
goLineUp: function(cm) {cm.moveV(-1"line");},
    
goLineDown: function(cm) {cm.moveV(1"line");},
    
goPageUp: function(cm) {cm.moveV(-1"page");},
    
goPageDown: function(cm) {cm.moveV(1"page");},
    
goCharLeft: function(cm) {cm.moveH(-1"char");},
    
goCharRight: function(cm) {cm.moveH(1"char");},
    
goColumnLeft: function(cm) {cm.moveH(-1"column");},
    
goColumnRight: function(cm) {cm.moveH(1"column");},
    
goWordLeft: function(cm) {cm.moveH(-1"word");},
    
goGroupRight: function(cm) {cm.moveH(1"group");},
    
goGroupLeft: function(cm) {cm.moveH(-1"group");},
    
goWordRight: function(cm) {cm.moveH(1"word");},
    
delCharBefore: function(cm) {cm.deleteH(-1"char");},
    
delCharAfter: function(cm) {cm.deleteH(1"char");},
    
delWordBefore: function(cm) {cm.deleteH(-1"word");},
    
delWordAfter: function(cm) {cm.deleteH(1"word");},
    
delGroupBefore: function(cm) {cm.deleteH(-1"group");},
    
delGroupAfter: function(cm) {cm.deleteH(1"group");},
    
indentAuto: function(cm) {cm.indentSelection("smart");},
    
indentMore: function(cm) {cm.indentSelection("add");},
    
indentLess: function(cm) {cm.indentSelection("subtract");},
    
insertTab: function(cm) {cm.replaceSelection("t");},
    
insertSoftTab: function(cm) {
      var 
spaces = [], ranges cm.listSelections(), tabSize cm.options.tabSize;
      for (var 
0ranges.lengthi++) {
        var 
pos ranges[i].from();
        var 
col countColumn(cm.getLine(pos.line), pos.chtabSize);
        
spaces.push(new Array(tabSize col tabSize 1).join(" "));
      }
      
cm.replaceSelections(spaces);
    },
    
defaultTab: function(cm) {
      if (
cm.somethingSelected()) cm.indentSelection("add");
      else 
cm.execCommand("insertTab");
    },
    
transposeChars: function(cm) {
      
runInOp(cm, function() {
        var 
ranges cm.listSelections(), newSel = [];
        for (var 
0ranges.lengthi++) {
          var 
cur ranges[i].headline getLine(cm.doccur.line).text;
          if (
line) {
            if (
cur.ch == line.lengthcur = new Pos(cur.linecur.ch 1);
            if (
cur.ch 0) {
              
cur = new Pos(cur.linecur.ch 1);
              
cm.replaceRange(line.charAt(cur.ch 1) + line.charAt(cur.ch 2),
                              
Pos(cur.linecur.ch 2), cur"+transpose");
            } else if (
cur.line cm.doc.first) {
              var 
prev getLine(cm.doccur.line 1).text;
              if (
prev)
                
cm.replaceRange(line.charAt(0) + "n" prev.charAt(prev.length 1),
                                
Pos(cur.line 1prev.length 1), Pos(cur.line1), "+transpose");
            }
          }
          
newSel.push(new Range(curcur));
        }
        
cm.setSelections(newSel);
      });
    },
    
newlineAndIndent: function(cm) {
      
runInOp(cm, function() {
        var 
len cm.listSelections().length;
        for (var 
0leni++) {
          var 
range cm.listSelections()[i];
          
cm.replaceRange("n"range.anchorrange.head"+input");
          
cm.indentLine(range.from().line 1nulltrue);
          
ensureCursorVisible(cm);
        }
      });
    },
    
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
  };

  
// STANDARD KEYMAPS

  
var keyMap CodeMirror.keyMap = {};
  
keyMap.basic = {
    
"Left""goCharLeft""Right""goCharRight""Up""goLineUp""Down""goLineDown",
    
"End""goLineEnd""Home""goLineStartSmart""PageUp""goPageUp""PageDown""goPageDown",
    
"Delete""delCharAfter""Backspace""delCharBefore""Shift-Backspace""delCharBefore",
    
"Tab""defaultTab""Shift-Tab""indentAuto",
    
"Enter""newlineAndIndent""Insert""toggleOverwrite",
    
"Esc""singleSelection"
  
};
  
// Note that the save and find-related commands aren't defined by
  // default. User code or addons can define them. Unknown commands
  // are simply ignored.
  
keyMap.pcDefault = {
    
"Ctrl-A""selectAll""Ctrl-D""deleteLine""Ctrl-Z""undo""Shift-Ctrl-Z""redo""Ctrl-Y""redo",
    
"Ctrl-Home""goDocStart""Ctrl-Up""goDocStart""Ctrl-End""goDocEnd""Ctrl-Down""goDocEnd",
    
"Ctrl-Left""goGroupLeft""Ctrl-Right""goGroupRight""Alt-Left""goLineStart""Alt-Right""goLineEnd",
    
"Ctrl-Backspace""delGroupBefore""Ctrl-Delete""delGroupAfter""Ctrl-S""save""Ctrl-F""find",
    
"Ctrl-G""findNext""Shift-Ctrl-G""findPrev""Shift-Ctrl-F""replace""Shift-Ctrl-R""replaceAll",
    
"Ctrl-[""indentLess""Ctrl-]""indentMore",
    
"Ctrl-U""undoSelection""Shift-Ctrl-U""redoSelection""Alt-U""redoSelection",
    
fallthrough"basic"
  
};
  
keyMap.macDefault = {
    
"Cmd-A""selectAll""Cmd-D""deleteLine""Cmd-Z""undo""Shift-Cmd-Z""redo""Cmd-Y""redo",
    
"Cmd-Up""goDocStart""Cmd-End""goDocEnd""Cmd-Down""goDocEnd""Alt-Left""goGroupLeft",
    
"Alt-Right""goGroupRight""Cmd-Left""goLineStart""Cmd-Right""goLineEnd""Alt-Backspace""delGroupBefore",
    
"Ctrl-Alt-Backspace""delGroupAfter""Alt-Delete""delGroupAfter""Cmd-S""save""Cmd-F""find",
    
"Cmd-G""findNext""Shift-Cmd-G""findPrev""Cmd-Alt-F""replace""Shift-Cmd-Alt-F""replaceAll",
    
"Cmd-[""indentLess""Cmd-]""indentMore""Cmd-Backspace""delLineLeft",
    
"Cmd-U""undoSelection""Shift-Cmd-U""redoSelection",
    
fallthrough: ["basic""emacsy"]
  };
  
// Very basic readline/emacs-style bindings, which are standard on Mac.
  
keyMap.emacsy = {
    
"Ctrl-F""goCharRight""Ctrl-B""goCharLeft""Ctrl-P""goLineUp""Ctrl-N""goLineDown",
    
"Alt-F""goWordRight""Alt-B""goWordLeft""Ctrl-A""goLineStart""Ctrl-E""goLineEnd",
    
"Ctrl-V""goPageDown""Shift-Ctrl-V""goPageUp""Ctrl-D""delCharAfter""Ctrl-H""delCharBefore",
    
"Alt-D""delWordAfter""Alt-Backspace""delWordBefore""Ctrl-K""killLine""Ctrl-T""transposeChars"
  
};
  
keyMap["default"] = mac keyMap.macDefault keyMap.pcDefault;

  
// KEYMAP DISPATCH

  
function getKeyMap(val) {
    if (
typeof val == "string") return keyMap[val];
    else return 
val;
  }

  
// Given an array of keymaps and a key name, call handle on any
  // bindings found, until that returns a truthy value, at which point
  // we consider the key handled. Implements things like binding a key
  // to false stopping further handling and keymap fallthrough.
  
var lookupKey CodeMirror.lookupKey = function(namemapshandle) {
    function 
lookup(map) {
      
map getKeyMap(map);
      var 
found map[name];
      if (
found === false) return "stop";
      if (
found != null && handle(found)) return true;
      if (
map.nofallthrough) return "stop";

      var 
fallthrough map.fallthrough;
      if (
fallthrough == null) return false;
      if (
Object.prototype.toString.call(fallthrough) != "[object Array]")
        return 
lookup(fallthrough);
      for (var 
0fallthrough.length; ++i) {
        var 
done lookup(fallthrough[i]);
        if (
done) return done;
      }
      return 
false;
    }

    for (var 
0maps.length; ++i) {
      var 
done lookup(maps[i]);
      if (
done) return done != "stop";
    }
  };

  
// Modifier key presses don't count as 'real' key presses for the
  // purpose of keymap fallthrough.
  
var isModifierKey CodeMirror.isModifierKey = function(event) {
    var 
name keyNames[event.keyCode];
    return 
name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
  };

  
// Look up the name of a key as indicated by an event object.
  
var keyName CodeMirror.keyName = function(eventnoShift) {
    if (
presto && event.keyCode == 34 && event["char"]) return false;
    var 
name keyNames[event.keyCode];
    if (
name == null || event.altGraphKey) return false;
    if (
event.altKeyname "Alt-" name;
    if (
flipCtrlCmd event.metaKey event.ctrlKeyname "Ctrl-" name;
    if (
flipCtrlCmd event.ctrlKey event.metaKeyname "Cmd-" name;
    if (!
noShift && event.shiftKeyname "Shift-" name;
    return 
name;
  };

  
// FROMTEXTAREA

  
CodeMirror.fromTextArea = function(textareaoptions) {
    if (!
optionsoptions = {};
    
options.value textarea.value;
    if (!
options.tabindex && textarea.tabindex)
      
options.tabindex textarea.tabindex;
    if (!
options.placeholder && textarea.placeholder)
      
options.placeholder textarea.placeholder;
    
// Set autofocus to true if this textarea is focused, or if it has
    // autofocus and no other element is focused.
    
if (options.autofocus == null) {
      var 
hasFocus activeElt();
      
options.autofocus hasFocus == textarea ||
        
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
    }

    function 
save() {textarea.value cm.getValue();}
    if (
textarea.form) {
      
on(textarea.form"submit"save);
      
// Deplorable hack to make the submit method do the right thing.
      
if (!options.leaveSubmitMethodAlone) {
        var 
form textarea.formrealSubmit form.submit;
        try {
          var 
wrappedSubmit form.submit = function() {
            
save();
            
form.submit realSubmit;
            
form.submit();
            
form.submit wrappedSubmit;
          };
        } catch(
e) {}
      }
    }

    
textarea.style.display "none";
    var 
cm CodeMirror(function(node) {
      
textarea.parentNode.insertBefore(nodetextarea.nextSibling);
    }, 
options);
    
cm.save save;
    
cm.getTextArea = function() { return textarea; };
    
cm.toTextArea = function() {
      
save();
      
textarea.parentNode.removeChild(cm.getWrapperElement());
      
textarea.style.display "";
      if (
textarea.form) {
        
off(textarea.form"submit"save);
        if (
typeof textarea.form.submit == "function")
          
textarea.form.submit realSubmit;
      }
    };
    return 
cm;
  };

  
// STRING STREAM

  // Fed to the mode parsers, provides helper functions to make
  // parsers more succinct.

  
var StringStream CodeMirror.StringStream = function(stringtabSize) {
    
this.pos this.start 0;
    
this.string string;
    
this.tabSize tabSize || 8;
    
this.lastColumnPos this.lastColumnValue 0;
    
this.lineStart 0;
  };

  
StringStream.prototype = {
    
eol: function() {return this.pos >= this.string.length;},
    
sol: function() {return this.pos == this.lineStart;},
    
peek: function() {return this.string.charAt(this.pos) || undefined;},
    
next: function() {
      if (
this.pos this.string.length)
        return 
this.string.charAt(this.pos++);
    },
    
eat: function(match) {
      var 
ch this.string.charAt(this.pos);
      if (
typeof match == "string") var ok ch == match;
      else var 
ok ch && (match.test match.test(ch) : match(ch));
      if (
ok) {++this.pos; return ch;}
    },
    
eatWhile: function(match) {
      var 
start this.pos;
      while (
this.eat(match)){}
      return 
this.pos start;
    },
    
eatSpace: function() {
      var 
start this.pos;
      while (/[
su00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
      return 
this.pos start;
    },
    
skipToEnd: function() {this.pos this.string.length;},
    
skipTo: function(ch) {
      var 
found this.string.indexOf(chthis.pos);
      if (
found > -1) {this.pos found; return true;}
    },
    
backUp: function(n) {this.pos -= n;},
    
column: function() {
      if (
this.lastColumnPos this.start) {
        
this.lastColumnValue countColumn(this.stringthis.startthis.tabSizethis.lastColumnPosthis.lastColumnValue);
        
this.lastColumnPos this.start;
      }
      return 
this.lastColumnValue - (this.lineStart countColumn(this.stringthis.lineStartthis.tabSize) : 0);
    },
    
indentation: function() {
      return 
countColumn(this.stringnullthis.tabSize) -
        (
this.lineStart countColumn(this.stringthis.lineStartthis.tabSize) : 0);
    },
    
match: function(patternconsumecaseInsensitive) {
      if (
typeof pattern == "string") {
        var 
cased = function(str) {return caseInsensitive str.toLowerCase() : str;};
        var 
substr this.string.substr(this.pospattern.length);
        if (
cased(substr) == cased(pattern)) {
          if (
consume !== falsethis.pos += pattern.length;
          return 
true;
        }
      } else {
        var 
match this.string.slice(this.pos).match(pattern);
        if (
match && match.index 0) return null;
        if (
match && consume !== falsethis.pos += match[0].length;
        return 
match;
      }
    },
    
current: function(){return this.string.slice(this.startthis.pos);},
    
hideFirstChars: function(ninner) {
      
this.lineStart += n;
      try { return 
inner(); }
      finally { 
this.lineStart -= n; }
    }
  };

  
// TEXTMARKERS

  // Created with markText and setBookmark methods. A TextMarker is a
  // handle that can be used to clear or find a marked position in the
  // document. Line objects hold arrays (markedSpans) containing
  // {from, to, marker} object pointing to such marker objects, and
  // indicating that such a marker is present on that line. Multiple
  // lines may point to the same marker when it spans across lines.
  // The spans will have null for their from/to properties when the
  // marker continues beyond the start/end of the line. Markers have
  // links back to the lines they currently touch.

  
var TextMarker CodeMirror.TextMarker = function(doctype) {
    
this.lines = [];
    
this.type type;
    
this.doc doc;
  };
  
eventMixin(TextMarker);

  
// Clear the marker.
  
TextMarker.prototype.clear = function() {
    if (
this.explicitlyCleared) return;
    var 
cm this.doc.cmwithOp cm && !cm.curOp;
    if (
withOpstartOperation(cm);
    if (
hasHandler(this"clear")) {
      var 
found this.find();
      if (
foundsignalLater(this"clear"found.fromfound.to);
    }
    var 
min nullmax null;
    for (var 
0this.lines.length; ++i) {
      var 
line this.lines[i];
      var 
span getMarkedSpanFor(line.markedSpansthis);
      if (
cm && !this.collapsedregLineChange(cmlineNo(line), "text");
      else if (
cm) {
        if (
span.to != nullmax lineNo(line);
        if (
span.from != nullmin lineNo(line);
      }
      
line.markedSpans removeMarkedSpan(line.markedSpansspan);
      if (
span.from == null && this.collapsed && !lineIsHidden(this.docline) && cm)
        
updateLineHeight(linetextHeight(cm.display));
    }
    if (
cm && this.collapsed && !cm.options.lineWrapping) for (var 0this.lines.length; ++i) {
      var 
visual visualLine(this.lines[i]), len lineLength(visual);
      if (
len cm.display.maxLineLength) {
        
cm.display.maxLine visual;
        
cm.display.maxLineLength len;
        
cm.display.maxLineChanged true;
      }
    }

    if (
min != null && cm && this.collapsedregChange(cmminmax 1);
    
this.lines.length 0;
    
this.explicitlyCleared true;
    if (
this.atomic && this.doc.cantEdit) {
      
this.doc.cantEdit false;
      if (
cmreCheckSelection(cm.doc);
    }
    if (
cmsignalLater(cm"markerCleared"cmthis);
    if (
withOpendOperation(cm);
    if (
this.parentthis.parent.clear();
  };

  
// Find the position of the marker in the document. Returns a {from,
  // to} object by default. Side can be passed to get a specific side
  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
  // Pos objects returned contain a line object, rather than a line
  // number (used to prevent looking up the same line twice).
  
TextMarker.prototype.find = function(sidelineObj) {
    if (
side == null && this.type == "bookmark"side 1;
    var 
fromto;
    for (var 
0this.lines.length; ++i) {
      var 
line this.lines[i];
      var 
span getMarkedSpanFor(line.markedSpansthis);
      if (
span.from != null) {
        
from Pos(lineObj line lineNo(line), span.from);
        if (
side == -1) return from;
      }
      if (
span.to != null) {
        
to Pos(lineObj line lineNo(line), span.to);
        if (
side == 1) return to;
      }
    }
    return 
from && {fromfromtoto};
  };

  
// Signals that the marker's widget changed, and surrounding layout
  // should be recomputed.
  
TextMarker.prototype.changed = function() {
    var 
pos this.find(-1true), widget thiscm this.doc.cm;
    if (!
pos || !cm) return;
    
runInOp(cm, function() {
      var 
line pos.linelineN lineNo(pos.line);
      var 
view findViewForLine(cmlineN);
      if (
view) {
        
clearLineMeasurementCacheFor(view);
        
cm.curOp.selectionChanged cm.curOp.forceUpdate true;
      }
      
cm.curOp.updateMaxLine true;
      if (!
lineIsHidden(widget.docline) && widget.height != null) {
        var 
oldHeight widget.height;
        
widget.height null;
        var 
dHeight widgetHeight(widget) - oldHeight;
        if (
dHeight)
          
updateLineHeight(lineline.height dHeight);
      }
    });
  };

  
TextMarker.prototype.attachLine = function(line) {
    if (!
this.lines.length && this.doc.cm) {
      var 
op this.doc.cm.curOp;
      if (!
op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkersthis) == -1)
        (
op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
    }
    
this.lines.push(line);
  };
  
TextMarker.prototype.detachLine = function(line) {
    
this.lines.splice(indexOf(this.linesline), 1);
    if (!
this.lines.length && this.doc.cm) {
      var 
op this.doc.cm.curOp;
      (
op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
    }
  };

  
// Collapsed markers have unique ids, in order to be able to order
  // them, which is needed for uniquely determining an outer marker
  // when they overlap (they may nest, but not partially overlap).
  
var nextMarkerId 0;

  
// Create a marker, wire it up to the right lines, and
  
function markText(docfromtooptionstype) {
    
// Shared markers (across linked documents) are handled separately
    // (markTextShared will call out to this again, once per
    // document).
    
if (options && options.shared) return markTextShared(docfromtooptionstype);
    
// Ensure we are in an operation.
    
if (doc.cm && !doc.cm.curOp) return operation(doc.cmmarkText)(docfromtooptionstype);

    var 
marker = new TextMarker(doctype), diff cmp(fromto);
    if (
optionscopyObj(optionsmarkerfalse);
    
// Don't connect empty markers unless clearWhenEmpty is false
    
if (diff || diff == && marker.clearWhenEmpty !== false)
      return 
marker;
    if (
marker.replacedWith) {
      
// Showing up as a widget implies collapsed (widget replaces text)
      
marker.collapsed true;
      
marker.widgetNode elt("span", [marker.replacedWith], "CodeMirror-widget");
      if (!
options.handleMouseEventsmarker.widgetNode.ignoreEvents true;
      if (
options.insertLeftmarker.widgetNode.insertLeft true;
    }
    if (
marker.collapsed) {
      if (
conflictingCollapsedRange(docfrom.linefromtomarker) ||
          
from.line != to.line && conflictingCollapsedRange(docto.linefromtomarker))
        throw new 
Error("Inserting collapsed marker partially overlapping an existing one");
      
sawCollapsedSpans true;
    }

    if (
marker.addToHistory)
      
addChangeToHistory(doc, {fromfromtotoorigin"markText"}, doc.selNaN);

    var 
curLine from.linecm doc.cmupdateMaxLine;
    
doc.iter(curLineto.line 1, function(line) {
      if (
cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
        
updateMaxLine true;
      if (
marker.collapsed && curLine != from.lineupdateLineHeight(line0);
      
addMarkedSpan(line, new MarkedSpan(marker,
                                         
curLine == from.line from.ch null,
                                         
curLine == to.line to.ch null));
      ++
curLine;
    });
    
// lineIsHidden depends on the presence of the spans, so needs a second pass
    
if (marker.collapseddoc.iter(from.lineto.line 1, function(line) {
      if (
lineIsHidden(docline)) updateLineHeight(line0);
    });

    if (
marker.clearOnEnteron(marker"beforeCursorEnter", function() { marker.clear(); });

    if (
marker.readOnly) {
      
sawReadOnlySpans true;
      if (
doc.history.done.length || doc.history.undone.length)
        
doc.clearHistory();
    }
    if (
marker.collapsed) {
      
marker.id = ++nextMarkerId;
      
marker.atomic true;
    }
    if (
cm) {
      
// Sync editor state
      
if (updateMaxLinecm.curOp.updateMaxLine true;
      if (
marker.collapsed)
        
regChange(cmfrom.lineto.line 1);
      else if (
marker.className || marker.title || marker.startStyle || marker.endStyle)
        for (var 
from.line<= to.linei++) regLineChange(cmi"text");
      if (
marker.atomicreCheckSelection(cm.doc);
      
signalLater(cm"markerAdded"cmmarker);
    }
    return 
marker;
  }

  
// SHARED TEXTMARKERS

  // A shared marker spans multiple linked documents. It is
  // implemented as a meta-marker-object controlling multiple normal
  // markers.
  
var SharedTextMarker CodeMirror.SharedTextMarker = function(markersprimary) {
    
this.markers markers;
    
this.primary primary;
    for (var 
0markers.length; ++i)
      
markers[i].parent this;
  };
  
eventMixin(SharedTextMarker);

  
SharedTextMarker.prototype.clear = function() {
    if (
this.explicitlyCleared) return;
    
this.explicitlyCleared true;
    for (var 
0this.markers.length; ++i)
      
this.markers[i].clear();
    
signalLater(this"clear");
  };
  
SharedTextMarker.prototype.find = function(sidelineObj) {
    return 
this.primary.find(sidelineObj);
  };

  function 
markTextShared(docfromtooptionstype) {
    
options copyObj(options);
    
options.shared false;
    var 
markers = [markText(docfromtooptionstype)], primary markers[0];
    var 
widget options.widgetNode;
    
linkedDocs(doc, function(doc) {
      if (
widgetoptions.widgetNode widget.cloneNode(true);
      
markers.push(markText(docclipPos(docfrom), clipPos(docto), optionstype));
      for (var 
0doc.linked.length; ++i)
        if (
doc.linked[i].isParent) return;
      
primary lst(markers);
    });
    return new 
SharedTextMarker(markersprimary);
  }

  function 
findSharedMarkers(doc) {
    return 
doc.findMarks(Pos(doc.first0), doc.clipPos(Pos(doc.lastLine())),
                         function(
m) { return m.parent; });
  }

  function 
copySharedMarkers(docmarkers) {
    for (var 
0markers.lengthi++) {
      var 
marker markers[i], pos marker.find();
      var 
mFrom doc.clipPos(pos.from), mTo doc.clipPos(pos.to);
      if (
cmp(mFrommTo)) {
        var 
subMark markText(docmFrommTomarker.primarymarker.primary.type);
        
marker.markers.push(subMark);
        
subMark.parent marker;
      }
    }
  }

  function 
detachSharedMarkers(markers) {
    for (var 
0markers.lengthi++) {
      var 
marker markers[i], linked = [marker.primary.doc];;
      
linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
      for (var 
0marker.markers.lengthj++) {
        var 
subMarker marker.markers[j];
        if (
indexOf(linkedsubMarker.doc) == -1) {
          
subMarker.parent null;
          
marker.markers.splice(j--, 1);
        }
      }
    }
  }

  
// TEXTMARKER SPANS

  
function MarkedSpan(markerfromto) {
    
this.marker marker;
    
this.from fromthis.to to;
  }

  
// Search an array of spans for a span matching the given marker.
  
function getMarkedSpanFor(spansmarker) {
    if (
spans) for (var 0spans.length; ++i) {
      var 
span spans[i];
      if (
span.marker == marker) return span;
    }
  }
  
// Remove a span from an array, returning undefined if no spans are
  // left (we don't store arrays for lines without spans).
  
function removeMarkedSpan(spansspan) {
    for (var 
r0spans.length; ++i)
      if (
spans[i] != span) (|| (= [])).push(spans[i]);
    return 
r;
  }
  
// Add a span to a line.
  
function addMarkedSpan(linespan) {
    
line.markedSpans line.markedSpans line.markedSpans.concat([span]) : [span];
    
span.marker.attachLine(line);
  }

  
// Used for the algorithm that adjusts markers for a change in the
  // document. These functions cut an array of spans at a given
  // character position, returning an array of remaining chunks (or
  // undefined if nothing remains).
  
function markedSpansBefore(oldstartChisInsert) {
    if (
old) for (var 0nwold.length; ++i) {
      var 
span old[i], marker span.marker;
      var 
startsBefore span.from == null || (marker.inclusiveLeft span.from <= startCh span.from startCh);
      if (
startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
        var 
endsAfter span.to == null || (marker.inclusiveRight span.to >= startCh span.to startCh);
        (
nw || (nw = [])).push(new MarkedSpan(markerspan.fromendsAfter null span.to));
      }
    }
    return 
nw;
  }
  function 
markedSpansAfter(oldendChisInsert) {
    if (
old) for (var 0nwold.length; ++i) {
      var 
span old[i], marker span.marker;
      var 
endsAfter span.to == null || (marker.inclusiveRight span.to >= endCh span.to endCh);
      if (
endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
        var 
startsBefore span.from == null || (marker.inclusiveLeft span.from <= endCh span.from endCh);
        (
nw || (nw = [])).push(new MarkedSpan(markerstartsBefore null span.from endCh,
                                              
span.to == null null span.to endCh));
      }
    }
    return 
nw;
  }

  
// Given a change object, compute the new set of marker spans that
  // cover the line in which the change took place. Removes spans
  // entirely within the change, reconnects spans belonging to the
  // same marker that appear on both sides of the change, and cuts off
  // spans partially within the change. Returns an array of span
  // arrays with one element for each line in (after) the change.
  
function stretchSpansOverChange(docchange) {
    var 
oldFirst isLine(docchange.from.line) && getLine(docchange.from.line).markedSpans;
    var 
oldLast isLine(docchange.to.line) && getLine(docchange.to.line).markedSpans;
    if (!
oldFirst && !oldLast) return null;

    var 
startCh change.from.chendCh change.to.chisInsert cmp(change.fromchange.to) == 0;
    
// Get the spans that 'stick out' on both sides
    
var first markedSpansBefore(oldFirststartChisInsert);
    var 
last markedSpansAfter(oldLastendChisInsert);

    
// Next, merge those two ends
    
var sameLine change.text.length == 1offset lst(change.text).length + (sameLine startCh 0);
    if (
first) {
      
// Fix up .to properties of first
      
for (var 0first.length; ++i) {
        var 
span first[i];
        if (
span.to == null) {
          var 
found getMarkedSpanFor(lastspan.marker);
          if (!
foundspan.to startCh;
          else if (
sameLinespan.to found.to == null null found.to offset;
        }
      }
    }
    if (
last) {
      
// Fix up .from in last (or move them into first in case of sameLine)
      
for (var 0last.length; ++i) {
        var 
span last[i];
        if (
span.to != nullspan.to += offset;
        if (
span.from == null) {
          var 
found getMarkedSpanFor(firstspan.marker);
          if (!
found) {
            
span.from offset;
            if (
sameLine) (first || (first = [])).push(span);
          }
        } else {
          
span.from += offset;
          if (
sameLine) (first || (first = [])).push(span);
        }
      }
    }
    
// Make sure we didn't create any zero-length spans
    
if (firstfirst clearEmptySpans(first);
    if (
last && last != firstlast clearEmptySpans(last);

    var 
newMarkers = [first];
    if (!
sameLine) {
      
// Fill gap with whole-line-spans
      
var gap change.text.length 2gapMarkers;
      if (
gap && first)
        for (var 
0first.length; ++i)
          if (
first[i].to == null)
            (
gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].markernullnull));
      for (var 
0gap; ++i)
        
newMarkers.push(gapMarkers);
      
newMarkers.push(last);
    }
    return 
newMarkers;
  }

  
// Remove spans that are empty and don't have a clearWhenEmpty
  // option of false.
  
function clearEmptySpans(spans) {
    for (var 
0spans.length; ++i) {
      var 
span spans[i];
      if (
span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
        
spans.splice(i--, 1);
    }
    if (!
spans.length) return null;
    return 
spans;
  }

  
// Used for un/re-doing changes from the history. Combines the
  // result of computing the existing spans with the set of spans that
  // existed in the history (so that deleting around a span and then
  // undoing brings back the span).
  
function mergeOldSpans(docchange) {
    var 
old getOldSpans(docchange);
    var 
stretched stretchSpansOverChange(docchange);
    if (!
old) return stretched;
    if (!
stretched) return old;

    for (var 
0old.length; ++i) {
      var 
oldCur old[i], stretchCur stretched[i];
      if (
oldCur && stretchCur) {
        
spans: for (var 0stretchCur.length; ++j) {
          var 
span stretchCur[j];
          for (var 
0oldCur.length; ++k)
            if (
oldCur[k].marker == span.marker) continue spans;
          
oldCur.push(span);
        }
      } else if (
stretchCur) {
        
old[i] = stretchCur;
      }
    }
    return 
old;
  }

  
// Used to 'clip' out readOnly ranges when making a change.
  
function removeReadOnlyRanges(docfromto) {
    var 
markers null;
    
doc.iter(from.lineto.line 1, function(line) {
      if (
line.markedSpans) for (var 0line.markedSpans.length; ++i) {
        var 
mark line.markedSpans[i].marker;
        if (
mark.readOnly && (!markers || indexOf(markersmark) == -1))
          (
markers || (markers = [])).push(mark);
      }
    });
    if (!
markers) return null;
    var 
parts = [{fromfromtoto}];
    for (var 
0markers.length; ++i) {
      var 
mk markers[i], mk.find(0);
      for (var 
0parts.length; ++j) {
        var 
parts[j];
        if (
cmp(p.tom.from) < || cmp(p.fromm.to) > 0) continue;
        var 
newParts = [j1], dfrom cmp(p.fromm.from), dto cmp(p.tom.to);
        if (
dfrom || !mk.inclusiveLeft && !dfrom)
          
newParts.push({fromp.fromtom.from});
        if (
dto || !mk.inclusiveRight && !dto)
          
newParts.push({fromm.totop.to});
        
parts.splice.apply(partsnewParts);
        
+= newParts.length 1;
      }
    }
    return 
parts;
  }

  
// Connect or disconnect spans from a line.
  
function detachMarkedSpans(line) {
    var 
spans line.markedSpans;
    if (!
spans) return;
    for (var 
0spans.length; ++i)
      
spans[i].marker.detachLine(line);
    
line.markedSpans null;
  }
  function 
attachMarkedSpans(linespans) {
    if (!
spans) return;
    for (var 
0spans.length; ++i)
      
spans[i].marker.attachLine(line);
    
line.markedSpans spans;
  }

  
// Helpers used when computing which overlapping collapsed span
  // counts as the larger one.
  
function extraLeft(marker) { return marker.inclusiveLeft ? -0; }
  function 
extraRight(marker) { return marker.inclusiveRight 0; }

  
// Returns a number indicating which of two overlapping collapsed
  // spans is larger (and thus includes the other). Falls back to
  // comparing ids when the spans cover exactly the same range.
  
function compareCollapsedMarkers(ab) {
    var 
lenDiff a.lines.length b.lines.length;
    if (
lenDiff != 0) return lenDiff;
    var 
aPos a.find(), bPos b.find();
    var 
fromCmp cmp(aPos.frombPos.from) || extraLeft(a) - extraLeft(b);
    if (
fromCmp) return -fromCmp;
    var 
toCmp cmp(aPos.tobPos.to) || extraRight(a) - extraRight(b);
    if (
toCmp) return toCmp;
    return 
b.id a.id;
  }

  
// Find out whether a line ends or starts in a collapsed span. If
  // so, return the marker for that span.
  
function collapsedSpanAtSide(linestart) {
    var 
sps sawCollapsedSpans && line.markedSpansfound;
    if (
sps) for (var sp0sps.length; ++i) {
      
sp sps[i];
      if (
sp.marker.collapsed && (start sp.from sp.to) == null &&
          (!
found || compareCollapsedMarkers(foundsp.marker) < 0))
        
found sp.marker;
    }
    return 
found;
  }
  function 
collapsedSpanAtStart(line) { return collapsedSpanAtSide(linetrue); }
  function 
collapsedSpanAtEnd(line) { return collapsedSpanAtSide(linefalse); }

  
// Test whether there exists a collapsed span that partially
  // overlaps (covers the start or end, but not both) of a new span.
  // Such overlap is not allowed.
  
function conflictingCollapsedRange(doclineNofromtomarker) {
    var 
line getLine(doclineNo);
    var 
sps sawCollapsedSpans && line.markedSpans;
    if (
sps) for (var 0sps.length; ++i) {
      var 
sp sps[i];
      if (!
sp.marker.collapsed) continue;
      var 
found sp.marker.find(0);
      var 
fromCmp cmp(found.fromfrom) || extraLeft(sp.marker) - extraLeft(marker);
      var 
toCmp cmp(found.toto) || extraRight(sp.marker) - extraRight(marker);
      if (
fromCmp >= && toCmp <= || fromCmp <= && toCmp >= 0) continue;
      if (
fromCmp <= && (cmp(found.tofrom) || extraRight(sp.marker) - extraLeft(marker)) > ||
          
fromCmp >= && (cmp(found.fromto) || extraLeft(sp.marker) - extraRight(marker)) < 0)
        return 
true;
    }
  }

  
// A visual line is a line as drawn on the screen. Folding, for
  // example, can cause multiple logical lines to appear on the same
  // visual line. This finds the start of the visual line that the
  // given line is part of (usually that is the line itself).
  
function visualLine(line) {
    var 
merged;
    while (
merged collapsedSpanAtStart(line))
      
line merged.find(-1true).line;
    return 
line;
  }

  
// Returns an array of logical lines that continue the visual line
  // started by the argument, or undefined if there are no such lines.
  
function visualLineContinued(line) {
    var 
mergedlines;
    while (
merged collapsedSpanAtEnd(line)) {
      
line merged.find(1true).line;
      (
lines || (lines = [])).push(line);
    }
    return 
lines;
  }

  
// Get the line number of the start of the visual line that the
  // given line number is part of.
  
function visualLineNo(doclineN) {
    var 
line getLine(doclineN), vis visualLine(line);
    if (
line == vis) return lineN;
    return 
lineNo(vis);
  }
  
// Get the line number of the start of the next visual line after
  // the given line.
  
function visualLineEndNo(doclineN) {
    if (
lineN doc.lastLine()) return lineN;
    var 
line getLine(doclineN), merged;
    if (!
lineIsHidden(docline)) return lineN;
    while (
merged collapsedSpanAtEnd(line))
      
line merged.find(1true).line;
    return 
lineNo(line) + 1;
  }

  
// Compute whether a line is hidden. Lines count as hidden when they
  // are part of a visual line that starts with another line, or when
  // they are entirely covered by collapsed, non-widget span.
  
function lineIsHidden(docline) {
    var 
sps sawCollapsedSpans && line.markedSpans;
    if (
sps) for (var sp0sps.length; ++i) {
      
sp sps[i];
      if (!
sp.marker.collapsed) continue;
      if (
sp.from == null) return true;
      if (
sp.marker.widgetNode) continue;
      if (
sp.from == && sp.marker.inclusiveLeft && lineIsHiddenInner(doclinesp))
        return 
true;
    }
  }
  function 
lineIsHiddenInner(doclinespan) {
    if (
span.to == null) {
      var 
end span.marker.find(1true);
      return 
lineIsHiddenInner(docend.linegetMarkedSpanFor(end.line.markedSpansspan.marker));
    }
    if (
span.marker.inclusiveRight && span.to == line.text.length)
      return 
true;
    for (var 
sp0line.markedSpans.length; ++i) {
      
sp line.markedSpans[i];
      if (
sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
          (
sp.to == null || sp.to != span.from) &&
          (
sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
          
lineIsHiddenInner(doclinesp)) return true;
    }
  }

  
// LINE WIDGETS

  // Line widgets are block elements displayed above or below a line.

  
var LineWidget CodeMirror.LineWidget = function(cmnodeoptions) {
    if (
options) for (var opt in options) if (options.hasOwnProperty(opt))
      
this[opt] = options[opt];
    
this.cm cm;
    
this.node node;
  };
  
eventMixin(LineWidget);

  function 
adjustScrollWhenAboveVisible(cmlinediff) {
    if (
heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
      
addToScrollPos(cmnulldiff);
  }

  
LineWidget.prototype.clear = function() {
    var 
cm this.cmws this.line.widgetsline this.lineno lineNo(line);
    if (
no == null || !ws) return;
    for (var 
0ws.length; ++i) if (ws[i] == thisws.splice(i--, 1);
    if (!
ws.lengthline.widgets null;
    var 
height widgetHeight(this);
    
runInOp(cm, function() {
      
adjustScrollWhenAboveVisible(cmline, -height);
      
regLineChange(cmno"widget");
      
updateLineHeight(lineMath.max(0line.height height));
    });
  };
  
LineWidget.prototype.changed = function() {
    var 
oldH this.heightcm this.cmline this.line;
    
this.height null;
    var 
diff widgetHeight(this) - oldH;
    if (!
diff) return;
    
runInOp(cm, function() {
      
cm.curOp.forceUpdate true;
      
adjustScrollWhenAboveVisible(cmlinediff);
      
updateLineHeight(lineline.height diff);
    });
  };

  function 
widgetHeight(widget) {
    if (
widget.height != null) return widget.height;
    if (!
contains(document.bodywidget.node))
      
removeChildrenAndAdd(widget.cm.display.measureelt("div", [widget.node], null"position: relative"));
    return 
widget.height widget.node.offsetHeight;
  }

  function 
addLineWidget(cmhandlenodeoptions) {
    var 
widget = new LineWidget(cmnodeoptions);
    if (
widget.noHScrollcm.display.alignWidgets true;
    
changeLine(cmhandle"widget", function(line) {
      var 
widgets line.widgets || (line.widgets = []);
      if (
widget.insertAt == nullwidgets.push(widget);
      else 
widgets.splice(Math.min(widgets.length 1Math.max(0widget.insertAt)), 0widget);
      
widget.line line;
      if (!
lineIsHidden(cm.docline)) {
        var 
aboveVisible heightAtLine(line) < cm.doc.scrollTop;
        
updateLineHeight(lineline.height widgetHeight(widget));
        if (
aboveVisibleaddToScrollPos(cmnullwidget.height);
        
cm.curOp.forceUpdate true;
      }
      return 
true;
    });
    return 
widget;
  }

  
// LINE DATA STRUCTURE

  // Line objects. These hold state related to a line, including
  // highlighting info (the styles array).
  
var Line CodeMirror.Line = function(textmarkedSpansestimateHeight) {
    
this.text text;
    
attachMarkedSpans(thismarkedSpans);
    
this.height estimateHeight estimateHeight(this) : 1;
  };
  
eventMixin(Line);
  
Line.prototype.lineNo = function() { return lineNo(this); };

  
// Change the content (text, markers) of a line. Automatically
  // invalidates cached information and tries to re-estimate the
  // line's height.
  
function updateLine(linetextmarkedSpansestimateHeight) {
    
line.text text;
    if (
line.stateAfterline.stateAfter null;
    if (
line.stylesline.styles null;
    if (
line.order != nullline.order null;
    
detachMarkedSpans(line);
    
attachMarkedSpans(linemarkedSpans);
    var 
estHeight estimateHeight estimateHeight(line) : 1;
    if (
estHeight != line.heightupdateLineHeight(lineestHeight);
  }

  
// Detach a line from the document tree and its markers.
  
function cleanUpLine(line) {
    
line.parent null;
    
detachMarkedSpans(line);
  }

  function 
extractLineClasses(typeoutput) {
    if (
type) for (;;) {
      var 
lineClass type.match(/(?:^|s+)line-(background-)?(S+)/);
      if (!
lineClass) break;
      
type type.slice(0lineClass.index) + type.slice(lineClass.index lineClass[0].length);
      var 
prop lineClass[1] ? "bgClass" "textClass";
      if (
output[prop] == null)
        
output[prop] = lineClass[2];
      else if (!(new 
RegExp("(?:^|s)" lineClass[2] + "(?:$|s)")).test(output[prop]))
        
output[prop] += " " lineClass[2];
    }
    return 
type;
  }

  function 
callBlankLine(modestate) {
    if (
mode.blankLine) return mode.blankLine(state);
    if (!
mode.innerMode) return;
    var 
inner CodeMirror.innerMode(modestate);
    if (
inner.mode.blankLine) return inner.mode.blankLine(inner.state);
  }

  function 
readToken(modestreamstate) {
    for (var 
010i++) {
      var 
style mode.token(streamstate);
      if (
stream.pos stream.start) return style;
    }
    throw new 
Error("Mode " mode.name " failed to advance stream.");
  }

  
// Run the given mode's parser over a line, calling f for each token.
  
function runMode(cmtextmodestateflineClassesforceToEnd) {
    var 
flattenSpans mode.flattenSpans;
    if (
flattenSpans == nullflattenSpans cm.options.flattenSpans;
    var 
curStart 0curStyle null;
    var 
stream = new StringStream(textcm.options.tabSize), style;
    if (
text == ""extractLineClasses(callBlankLine(modestate), lineClasses);
    while (!
stream.eol()) {
      if (
stream.pos cm.options.maxHighlightLength) {
        
flattenSpans false;
        if (
forceToEndprocessLine(cmtextstatestream.pos);
        
stream.pos text.length;
        
style null;
      } else {
        
style extractLineClasses(readToken(modestreamstate), lineClasses);
      }
      if (
cm.options.addModeClass) {
        var 
mName CodeMirror.innerMode(modestate).mode.name;
        if (
mNamestyle "m-" + (style mName " " style mName);
      }
      if (!
flattenSpans || curStyle != style) {
        if (
curStart stream.startf(stream.startcurStyle);
        
curStart stream.startcurStyle style;
      }
      
stream.start stream.pos;
    }
    while (
curStart stream.pos) {
      
// Webkit seems to refuse to render text nodes longer than 57444 characters
      
var pos Math.min(stream.poscurStart 50000);
      
f(poscurStyle);
      
curStart pos;
    }
  }

  
// Compute a style array (an array starting with a mode generation
  // -- for invalidation -- followed by pairs of end positions and
  // style strings), which is used to highlight the tokens on the
  // line.
  
function highlightLine(cmlinestateforceToEnd) {
    
// A styles array always starts with a number identifying the
    // mode/overlays that it is based on (for easy invalidation).
    
var st = [cm.state.modeGen], lineClasses = {};
    
// Compute the base array of styles
    
runMode(cmline.textcm.doc.modestate, function(endstyle) {
      
st.push(endstyle);
    }, 
lineClassesforceToEnd);

    
// Run overlays, adjust style array.
    
for (var 0cm.state.overlays.length; ++o) {
      var 
overlay cm.state.overlays[o], 1at 0;
      
runMode(cmline.textoverlay.modetrue, function(endstyle) {
        var 
start i;
        
// Ensure there's a token end at the current position, and that i points at it
        
while (at end) {
          var 
i_end st[i];
          if (
i_end end)
            
st.splice(i1endst[i+1], i_end);
          
+= 2;
          
at Math.min(endi_end);
        }
        if (!
style) return;
        if (
overlay.opaque) {
          
st.splice(startstartend"cm-overlay " style);
          
start 2;
        } else {
          for (; 
start istart += 2) {
            var 
cur st[start+1];
            
st[start+1] = (cur cur " " "") + "cm-overlay " style;
          }
        }
      }, 
lineClasses);
    }

    return {
stylesstclasseslineClasses.bgClass || lineClasses.textClass lineClasses null};
  }

  function 
getLineStyles(cmline) {
    if (!
line.styles || line.styles[0] != cm.state.modeGen) {
      var 
result highlightLine(cmlineline.stateAfter getStateBefore(cmlineNo(line)));
      
line.styles result.styles;
      if (
result.classesline.styleClasses result.classes;
      else if (
line.styleClassesline.styleClasses null;
    }
    return 
line.styles;
  }

  
// Lightweight form of highlight -- proceed over this line and
  // update state, but don't save a style array. Used for lines that
  // aren't currently visible.
  
function processLine(cmtextstatestartAt) {
    var 
mode cm.doc.mode;
    var 
stream = new StringStream(textcm.options.tabSize);
    
stream.start stream.pos startAt || 0;
    if (
text == ""callBlankLine(modestate);
    while (!
stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
      
readToken(modestreamstate);
      
stream.start stream.pos;
    }
  }

  
// Convert a style as returned by a mode (either null, or a string
  // containing one or more styles) to a CSS style. This is cached,
  // and also looks for line-wide styles.
  
var styleToClassCache = {}, styleToClassCacheWithMode = {};
  function 
interpretTokenStyle(styleoptions) {
    if (!
style || /^s*$/.test(style)) return null;
    var 
cache options.addModeClass styleToClassCacheWithMode styleToClassCache;
    return 
cache[style] ||
      (
cache[style] = style.replace(/S+/g"cm-$&"));
  }

  
// Render the DOM representation of the text of a line. Also builds
  // up a 'line map', which points at the DOM nodes that represent
  // specific stretches of text, and is used by the measuring code.
  // The returned object contains the DOM node, this map, and
  // information about line-wide styles that were set by the mode.
  
function buildLineContent(cmlineView) {
    
// The padding-right forces the element to have a 'border', which
    // is needed on Webkit to be able to get line-level bounding
    // rectangles for it (in measureChar).
    
var content elt("span"nullnullwebkit "padding-right: .1px" null);
    var 
builder = {preelt("pre", [content]), contentcontentcol0pos0cmcm};
    
lineView.measure = {};

    
// Iterate over the logical lines that make up this visual line.
    
for (var 0<= (lineView.rest lineView.rest.length 0); i++) {
      var 
line lineView.rest[1] : lineView.lineorder;
      
builder.pos 0;
      
builder.addToken buildToken;
      
// Optionally wire in some hacks into the token-rendering
      // algorithm, to deal with browser quirks.
      
if ((ie || webkit) && cm.getOption("lineWrapping"))
        
builder.addToken buildTokenSplitSpaces(builder.addToken);
      if (
hasBadBidiRects(cm.display.measure) && (order getOrder(line)))
        
builder.addToken buildTokenBadBidi(builder.addTokenorder);
      
builder.map = [];
      
insertLineContent(linebuildergetLineStyles(cmline));
      if (
line.styleClasses) {
        if (
line.styleClasses.bgClass)
          
builder.bgClass joinClasses(line.styleClasses.bgClassbuilder.bgClass || "");
        if (
line.styleClasses.textClass)
          
builder.textClass joinClasses(line.styleClasses.textClassbuilder.textClass || "");
      }

      
// Ensure at least a single node is present, for measuring.
      
if (builder.map.length == 0)
        
builder.map.push(00builder.content.appendChild(zeroWidthElement(cm.display.measure)));

      
// Store the map and a cache object for the current logical line
      
if (== 0) {
        
lineView.measure.map builder.map;
        
lineView.measure.cache = {};
      } else {
        (
lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
        (
lineView.measure.caches || (lineView.measure.caches = [])).push({});
      }
    }

    
signal(cm"renderLine"cmlineView.linebuilder.pre);
    if (
builder.pre.className)
      
builder.textClass joinClasses(builder.pre.classNamebuilder.textClass || "");
    return 
builder;
  }

  function 
defaultSpecialCharPlaceholder(ch) {
    var 
token elt("span""u2022""cm-invalidchar");
    
token.title "\u" ch.charCodeAt(0).toString(16);
    return 
token;
  }

  
// Build up the DOM representation for a single token, and add it to
  // the line map. Takes care to render special characters separately.
  
function buildToken(buildertextstylestartStyleendStyletitle) {
    if (!
text) return;
    var 
special builder.cm.options.specialCharsmustWrap false;
    if (!
special.test(text)) {
      
builder.col += text.length;
      var 
content document.createTextNode(text);
      
builder.map.push(builder.posbuilder.pos text.lengthcontent);
      if (
ie_upto8mustWrap true;
      
builder.pos += text.length;
    } else {
      var 
content document.createDocumentFragment(), pos 0;
      while (
true) {
        
special.lastIndex pos;
        var 
special.exec(text);
        var 
skipped m.index pos text.length pos;
        if (
skipped) {
          var 
txt document.createTextNode(text.slice(pospos skipped));
          if (
ie_upto8content.appendChild(elt("span", [txt]));
          else 
content.appendChild(txt);
          
builder.map.push(builder.posbuilder.pos skippedtxt);
          
builder.col += skipped;
          
builder.pos += skipped;
        }
        if (!
m) break;
        
pos += skipped 1;
        if (
m[0] == "t") {
          var 
tabSize builder.cm.options.tabSizetabWidth tabSize builder.col tabSize;
          var 
txt content.appendChild(elt("span"spaceStr(tabWidth), "cm-tab"));
          
builder.col += tabWidth;
        } else {
          var 
txt builder.cm.options.specialCharPlaceholder(m[0]);
          if (
ie_upto8content.appendChild(elt("span", [txt]));
          else 
content.appendChild(txt);
          
builder.col += 1;
        }
        
builder.map.push(builder.posbuilder.pos 1txt);
        
builder.pos++;
      }
    }
    if (
style || startStyle || endStyle || mustWrap) {
      var 
fullStyle style || "";
      if (
startStylefullStyle += startStyle;
      if (
endStylefullStyle += endStyle;
      var 
token elt("span", [content], fullStyle);
      if (
titletoken.title title;
      return 
builder.content.appendChild(token);
    }
    
builder.content.appendChild(content);
  }

  function 
buildTokenSplitSpaces(inner) {
    function 
split(old) {
      var 
out " ";
      for (var 
0old.length 2; ++iout += " " "u00a0";
      
out += " ";
      return 
out;
    }
    return function(
buildertextstylestartStyleendStyletitle) {
      
inner(buildertext.replace(/ {3,}/gsplit), stylestartStyleendStyletitle);
    };
  }

  
// Work around nonsense dimensions being reported for stretches of
  // right-to-left text.
  
function buildTokenBadBidi(innerorder) {
    return function(
buildertextstylestartStyleendStyletitle) {
      
style style style " cm-force-border" "cm-force-border";
      var 
start builder.posend start text.length;
      for (;;) {
        
// Find the part that overlaps with the start of this text
        
for (var 0order.lengthi++) {
          var 
part order[i];
          if (
part.to start && part.from <= start) break;
        }
        if (
part.to >= end) return inner(buildertextstylestartStyleendStyletitle);
        
inner(buildertext.slice(0part.to start), stylestartStylenulltitle);
        
startStyle null;
        
text text.slice(part.to start);
        
start part.to;
      }
    };
  }

  function 
buildCollapsedSpan(buildersizemarkerignoreWidget) {
    var 
widget = !ignoreWidget && marker.widgetNode;
    if (
widget) {
      
builder.map.push(builder.posbuilder.pos sizewidget);
      
builder.content.appendChild(widget);
    }
    
builder.pos += size;
  }

  
// Outputs a number of spans to make up a line, taking highlighting
  // and marked text into account.
  
function insertLineContent(linebuilderstyles) {
    var 
spans line.markedSpansallText line.textat 0;
    if (!
spans) {
      for (var 
1styles.lengthi+=2)
        
builder.addToken(builderallText.slice(atat styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
      return;
    }

    var 
len allText.lengthpos 01text ""style;
    var 
nextChange 0spanStylespanEndStylespanStartStyletitlecollapsed;
    for (;;) {
      if (
nextChange == pos) { // Update current marker set
        
spanStyle spanEndStyle spanStartStyle title "";
        
collapsed nullnextChange Infinity;
        var 
foundBookmarks = [];
        for (var 
0spans.length; ++j) {
          var 
sp spans[j], sp.marker;
          if (
sp.from <= pos && (sp.to == null || sp.to pos)) {
            if (
sp.to != null && nextChange sp.to) { nextChange sp.tospanEndStyle ""; }
            if (
m.classNamespanStyle += " " m.className;
            if (
m.startStyle && sp.from == posspanStartStyle += " " m.startStyle;
            if (
m.endStyle && sp.to == nextChangespanEndStyle += " " m.endStyle;
            if (
m.title && !titletitle m.title;
            if (
m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.markerm) < 0))
              
collapsed sp;
          } else if (
sp.from pos && nextChange sp.from) {
            
nextChange sp.from;
          }
          if (
m.type == "bookmark" && sp.from == pos && m.widgetNodefoundBookmarks.push(m);
        }
        if (
collapsed && (collapsed.from || 0) == pos) {
          
buildCollapsedSpan(builder, (collapsed.to == null len collapsed.to) - pos,
                             
collapsed.markercollapsed.from == null);
          if (
collapsed.to == null) return;
        }
        if (!
collapsed && foundBookmarks.length) for (var 0foundBookmarks.length; ++j)
          
buildCollapsedSpan(builder0foundBookmarks[j]);
      }
      if (
pos >= len) break;

      var 
upto Math.min(lennextChange);
      while (
true) {
        if (
text) {
          var 
end pos text.length;
          if (!
collapsed) {
            var 
tokenText end upto text.slice(0upto pos) : text;
            
builder.addToken(buildertokenTextstyle style spanStyle spanStyle,
                             
spanStartStylepos tokenText.length == nextChange spanEndStyle ""title);
          }
          if (
end >= upto) {text text.slice(upto pos); pos upto; break;}
          
pos end;
          
spanStartStyle "";
        }
        
text allText.slice(atat styles[i++]);
        
style interpretTokenStyle(styles[i++], builder.cm.options);
      }
    }
  }

  
// DOCUMENT DATA STRUCTURE

  // By default, updates that start and end at the beginning of a line
  // are treated specially, in order to make the association of line
  // widgets and marker elements with the text behave more intuitive.
  
function isWholeLineUpdate(docchange) {
    return 
change.from.ch == && change.to.ch == && lst(change.text) == "" &&
      (!
doc.cm || doc.cm.options.wholeLineUpdateBefore);
  }

  
// Perform a change on the document data structure.
  
function updateDoc(docchangemarkedSpansestimateHeight) {
    function 
spansFor(n) {return markedSpans markedSpans[n] : null;}
    function 
update(linetextspans) {
      
updateLine(linetextspansestimateHeight);
      
signalLater(line"change"linechange);
    }

    var 
from change.fromto change.totext change.text;
    var 
firstLine getLine(docfrom.line), lastLine getLine(docto.line);
    var 
lastText lst(text), lastSpans spansFor(text.length 1), nlines to.line from.line;

    
// Adjust the line structure
    
if (isWholeLineUpdate(docchange)) {
      
// This is a whole-line replace. Treated specially to make
      // sure line objects move the way they are supposed to.
      
for (var 0added = []; text.length 1; ++i)
        
added.push(new Line(text[i], spansFor(i), estimateHeight));
      
update(lastLinelastLine.textlastSpans);
      if (
nlinesdoc.remove(from.linenlines);
      if (
added.lengthdoc.insert(from.lineadded);
    } else if (
firstLine == lastLine) {
      if (
text.length == 1) {
        
update(firstLinefirstLine.text.slice(0from.ch) + lastText firstLine.text.slice(to.ch), lastSpans);
      } else {
        for (var 
added = [], 1text.length 1; ++i)
          
added.push(new Line(text[i], spansFor(i), estimateHeight));
        
added.push(new Line(lastText firstLine.text.slice(to.ch), lastSpansestimateHeight));
        
update(firstLinefirstLine.text.slice(0from.ch) + text[0], spansFor(0));
        
doc.insert(from.line 1added);
      }
    } else if (
text.length == 1) {
      
update(firstLinefirstLine.text.slice(0from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
      
doc.remove(from.line 1nlines);
    } else {
      
update(firstLinefirstLine.text.slice(0from.ch) + text[0], spansFor(0));
      
update(lastLinelastText lastLine.text.slice(to.ch), lastSpans);
      for (var 
1added = []; text.length 1; ++i)
        
added.push(new Line(text[i], spansFor(i), estimateHeight));
      if (
nlines 1doc.remove(from.line 1nlines 1);
      
doc.insert(from.line 1added);
    }

    
signalLater(doc"change"docchange);
  }

  
// The document is represented as a BTree consisting of leaves, with
  // chunk of lines in them, and branches, with up to ten leaves or
  // other branch nodes below them. The top node is always a branch
  // node, and is the document object itself (meaning it has
  // additional methods and properties).
  //
  // All nodes have parent links. The tree is used both to go from
  // line numbers to line objects, and to go from objects to numbers.
  // It also indexes by height, and is used to convert between height
  // and line object, and to find the total height of the document.
  //
  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html

  
function LeafChunk(lines) {
    
this.lines lines;
    
this.parent null;
    for (var 
0height 0lines.length; ++i) {
      
lines[i].parent this;
      
height += lines[i].height;
    }
    
this.height height;
  }

  
LeafChunk.prototype = {
    
chunkSize: function() { return this.lines.length; },
    
// Remove the n lines at offset 'at'.
    
removeInner: function(atn) {
      for (var 
atat ne; ++i) {
        var 
line this.lines[i];
        
this.height -= line.height;
        
cleanUpLine(line);
        
signalLater(line"delete");
      }
      
this.lines.splice(atn);
    },
    
// Helper used to collapse a small branch into a single leaf.
    
collapse: function(lines) {
      
lines.push.apply(linesthis.lines);
    },
    
// Insert the given array of lines at offset 'at', count them as
    // having the given height.
    
insertInner: function(atlinesheight) {
      
this.height += height;
      
this.lines this.lines.slice(0at).concat(lines).concat(this.lines.slice(at));
      for (var 
0lines.length; ++ilines[i].parent this;
    },
    
// Used to iterate over a part of the tree.
    
iterN: function(atnop) {
      for (var 
at nat e; ++at)
        if (
op(this.lines[at])) return true;
    }
  };

  function 
BranchChunk(children) {
    
this.children children;
    var 
size 0height 0;
    for (var 
0children.length; ++i) {
      var 
ch children[i];
      
size += ch.chunkSize(); height += ch.height;
      
ch.parent this;
    }
    
this.size size;
    
this.height height;
    
this.parent null;
  }

  
BranchChunk.prototype = {
    
chunkSize: function() { return this.size; },
    
removeInner: function(atn) {
      
this.size -= n;
      for (var 
0this.children.length; ++i) {
        var 
child this.children[i], sz child.chunkSize();
        if (
at sz) {
          var 
rm Math.min(nsz at), oldHeight child.height;
          
child.removeInner(atrm);
          
this.height -= oldHeight child.height;
          if (
sz == rm) { this.children.splice(i--, 1); child.parent null; }
          if ((
-= rm) == 0) break;
          
at 0;
        } else 
at -= sz;
      }
      
// If the result is smaller than 25 lines, ensure that it is a
      // single leaf node.
      
if (this.size 25 &&
          (
this.children.length || !(this.children[0] instanceof LeafChunk))) {
        var 
lines = [];
        
this.collapse(lines);
        
this.children = [new LeafChunk(lines)];
        
this.children[0].parent this;
      }
    },
    
collapse: function(lines) {
      for (var 
0this.children.length; ++ithis.children[i].collapse(lines);
    },
    
insertInner: function(atlinesheight) {
      
this.size += lines.length;
      
this.height += height;
      for (var 
0this.children.length; ++i) {
        var 
child this.children[i], sz child.chunkSize();
        if (
at <= sz) {
          
child.insertInner(atlinesheight);
          if (
child.lines && child.lines.length 50) {
            while (
child.lines.length 50) {
              var 
spilled child.lines.splice(child.lines.length 2525);
              var 
newleaf = new LeafChunk(spilled);
              
child.height -= newleaf.height;
              
this.children.splice(10newleaf);
              
newleaf.parent this;
            }
            
this.maybeSpill();
          }
          break;
        }
        
at -= sz;
      }
    },
    
// When a node has grown, check whether it should be split.
    
maybeSpill: function() {
      if (
this.children.length <= 10) return;
      var 
me this;
      do {
        var 
spilled me.children.splice(me.children.length 55);
        var 
sibling = new BranchChunk(spilled);
        if (!
me.parent) { // Become the parent node
          
var copy = new BranchChunk(me.children);
          
copy.parent me;
          
me.children = [copysibling];
          
me copy;
        } else {
          
me.size -= sibling.size;
          
me.height -= sibling.height;
          var 
myIndex indexOf(me.parent.childrenme);
          
me.parent.children.splice(myIndex 10sibling);
        }
        
sibling.parent me.parent;
      } while (
me.children.length 10);
      
me.parent.maybeSpill();
    },
    
iterN: function(atnop) {
      for (var 
0this.children.length; ++i) {
        var 
child this.children[i], sz child.chunkSize();
        if (
at sz) {
          var 
used Math.min(nsz at);
          if (
child.iterN(atusedop)) return true;
          if ((
-= used) == 0) break;
          
at 0;
        } else 
at -= sz;
      }
    }
  };

  var 
nextDocId 0;
  var 
Doc CodeMirror.Doc = function(textmodefirstLine) {
    if (!(
this instanceof Doc)) return new Doc(textmodefirstLine);
    if (
firstLine == nullfirstLine 0;

    
BranchChunk.call(this, [new LeafChunk([new Line(""null)])]);
    
this.first firstLine;
    
this.scrollTop this.scrollLeft 0;
    
this.cantEdit false;
    
this.cleanGeneration 1;
    
this.frontier firstLine;
    var 
start Pos(firstLine0);
    
this.sel simpleSelection(start);
    
this.history = new History(null);
    
this.id = ++nextDocId;
    
this.modeOption mode;

    if (
typeof text == "string"text splitLines(text);
    
updateDoc(this, {fromstarttostarttexttext});
    
setSelection(thissimpleSelection(start), sel_dontScroll);
  };

  
Doc.prototype createObj(BranchChunk.prototype, {
    
constructorDoc,
    
// Iterate over the document. Supports two forms -- with only one
    // argument, it calls that for each line in the document. With
    // three, it iterates over the range given by the first two (with
    // the second being non-inclusive).
    
iter: function(fromtoop) {
      if (
opthis.iterN(from this.firstto fromop);
      else 
this.iterN(this.firstthis.first this.sizefrom);
    },

    
// Non-public interface for adding and removing lines.
    
insert: function(atlines) {
      var 
height 0;
      for (var 
0lines.length; ++iheight += lines[i].height;
      
this.insertInner(at this.firstlinesheight);
    },
    
remove: function(atn) { this.removeInner(at this.firstn); },

    
// From here, the methods are part of the public interface. Most
    // are also available from CodeMirror (editor) instances.

    
getValue: function(lineSep) {
      var 
lines getLines(thisthis.firstthis.first this.size);
      if (
lineSep === false) return lines;
      return 
lines.join(lineSep || "n");
    },
    
setValuedocMethodOp(function(code) {
      var 
top Pos(this.first0), last this.first this.size 1;
      
makeChange(this, {fromtoptoPos(lastgetLine(thislast).text.length),
                        
textsplitLines(code), origin"setValue"}, true);
      
setSelection(thissimpleSelection(top));
    }),
    
replaceRange: function(codefromtoorigin) {
      
from clipPos(thisfrom);
      
to to clipPos(thisto) : from;
      
replaceRange(thiscodefromtoorigin);
    },
    
getRange: function(fromtolineSep) {
      var 
lines getBetween(thisclipPos(thisfrom), clipPos(thisto));
      if (
lineSep === false) return lines;
      return 
lines.join(lineSep || "n");
    },

    
getLine: function(line) {var this.getLineHandle(line); return && l.text;},

    
getLineHandle: function(line) {if (isLine(thisline)) return getLine(thisline);},
    
getLineNumber: function(line) {return lineNo(line);},

    
getLineHandleVisualStart: function(line) {
      if (
typeof line == "number"line getLine(thisline);
      return 
visualLine(line);
    },

    
lineCount: function() {return this.size;},
    
firstLine: function() {return this.first;},
    
lastLine: function() {return this.first this.size 1;},

    
clipPos: function(pos) {return clipPos(thispos);},

    
getCursor: function(start) {
      var 
range this.sel.primary(), pos;
      if (
start == null || start == "head"pos range.head;
      else if (
start == "anchor"pos range.anchor;
      else if (
start == "end" || start == "to" || start === falsepos range.to();
      else 
pos range.from();
      return 
pos;
    },
    
listSelections: function() { return this.sel.ranges; },
    
somethingSelected: function() {return this.sel.somethingSelected();},

    
setCursordocMethodOp(function(linechoptions) {
      
setSimpleSelection(thisclipPos(thistypeof line == "number" Pos(linech || 0) : line), nulloptions);
    }),
    
setSelectiondocMethodOp(function(anchorheadoptions) {
      
setSimpleSelection(thisclipPos(thisanchor), clipPos(thishead || anchor), options);
    }),
    
extendSelectiondocMethodOp(function(headotheroptions) {
      
extendSelection(thisclipPos(thishead), other && clipPos(thisother), options);
    }),
    
extendSelectionsdocMethodOp(function(headsoptions) {
      
extendSelections(thisclipPosArray(thisheadsoptions));
    }),
    
extendSelectionsBydocMethodOp(function(foptions) {
      
extendSelections(thismap(this.sel.rangesf), options);
    }),
    
setSelectionsdocMethodOp(function(rangesprimaryoptions) {
      if (!
ranges.length) return;
      for (var 
0out = []; ranges.lengthi++)
        
out[i] = new Range(clipPos(thisranges[i].anchor),
                           
clipPos(thisranges[i].head));
      if (
primary == nullprimary Math.min(ranges.length 1this.sel.primIndex);
      
setSelection(thisnormalizeSelection(outprimary), options);
    }),
    
addSelectiondocMethodOp(function(anchorheadoptions) {
      var 
ranges this.sel.ranges.slice(0);
      
ranges.push(new Range(clipPos(thisanchor), clipPos(thishead || anchor)));
      
setSelection(thisnormalizeSelection(rangesranges.length 1), options);
    }),

    
getSelection: function(lineSep) {
      var 
ranges this.sel.rangeslines;
      for (var 
0ranges.lengthi++) {
        var 
sel getBetween(thisranges[i].from(), ranges[i].to());
        
lines lines lines.concat(sel) : sel;
      }
      if (
lineSep === false) return lines;
      else return 
lines.join(lineSep || "n");
    },
    
getSelections: function(lineSep) {
      var 
parts = [], ranges this.sel.ranges;
      for (var 
0ranges.lengthi++) {
        var 
sel getBetween(thisranges[i].from(), ranges[i].to());
        if (
lineSep !== falsesel sel.join(lineSep || "n");
        
parts[i] = sel;
      }
      return 
parts;
    },
    
replaceSelection: function(codecollapseorigin) {
      var 
dup = [];
      for (var 
0this.sel.ranges.lengthi++)
        
dup[i] = code;
      
this.replaceSelections(dupcollapseorigin || "+input");
    },
    
replaceSelectionsdocMethodOp(function(codecollapseorigin) {
      var 
changes = [], sel this.sel;
      for (var 
0sel.ranges.lengthi++) {
        var 
range sel.ranges[i];
        
changes[i] = {fromrange.from(), torange.to(), textsplitLines(code[i]), originorigin};
      }
      var 
newSel collapse && collapse != "end" && computeReplacedSel(thischangescollapse);
      for (var 
changes.length 1>= 0i--)
        
makeChange(thischanges[i]);
      if (
newSelsetSelectionReplaceHistory(thisnewSel);
      else if (
this.cmensureCursorVisible(this.cm);
    }),
    
undodocMethodOp(function() {makeChangeFromHistory(this"undo");}),
    
redodocMethodOp(function() {makeChangeFromHistory(this"redo");}),
    
undoSelectiondocMethodOp(function() {makeChangeFromHistory(this"undo"true);}),
    
redoSelectiondocMethodOp(function() {makeChangeFromHistory(this"redo"true);}),

    
setExtending: function(val) {this.extend val;},
    
getExtending: function() {return this.extend;},

    
historySize: function() {
      var 
hist this.historydone 0undone 0;
      for (var 
0hist.done.lengthi++) if (!hist.done[i].ranges) ++done;
      for (var 
0hist.undone.lengthi++) if (!hist.undone[i].ranges) ++undone;
      return {
undodoneredoundone};
    },
    
clearHistory: function() {this.history = new History(this.history.maxGeneration);},

    
markClean: function() {
      
this.cleanGeneration this.changeGeneration(true);
    },
    
changeGeneration: function(forceSplit) {
      if (
forceSplit)
        
this.history.lastOp this.history.lastOrigin null;
      return 
this.history.generation;
    },
    
isClean: function (gen) {
      return 
this.history.generation == (gen || this.cleanGeneration);
    },

    
getHistory: function() {
      return {
donecopyHistoryArray(this.history.done),
              
undonecopyHistoryArray(this.history.undone)};
    },
    
setHistory: function(histData) {
      var 
hist this.history = new History(this.history.maxGeneration);
      
hist.done copyHistoryArray(histData.done.slice(0), nulltrue);
      
hist.undone copyHistoryArray(histData.undone.slice(0), nulltrue);
    },

    
markText: function(fromtooptions) {
      return 
markText(thisclipPos(thisfrom), clipPos(thisto), options"range");
    },
    
setBookmark: function(posoptions) {
      var 
realOpts = {replacedWithoptions && (options.nodeType == null options.widget options),
                      
insertLeftoptions && options.insertLeft,
                      
clearWhenEmptyfalsesharedoptions && options.shared};
      
pos clipPos(thispos);
      return 
markText(thisposposrealOpts"bookmark");
    },
    
findMarksAt: function(pos) {
      
pos clipPos(thispos);
      var 
markers = [], spans getLine(thispos.line).markedSpans;
      if (
spans) for (var 0spans.length; ++i) {
        var 
span spans[i];
        if ((
span.from == null || span.from <= pos.ch) &&
            (
span.to == null || span.to >= pos.ch))
          
markers.push(span.marker.parent || span.marker);
      }
      return 
markers;
    },
    
findMarks: function(fromtofilter) {
      
from clipPos(thisfrom); to clipPos(thisto);
      var 
found = [], lineNo from.line;
      
this.iter(from.lineto.line 1, function(line) {
        var 
spans line.markedSpans;
        if (
spans) for (var 0spans.lengthi++) {
          var 
span spans[i];
          if (!(
lineNo == from.line && from.ch span.to ||
                
span.from == null && lineNo != from.line||
                
lineNo == to.line && span.from to.ch) &&
              (!
filter || filter(span.marker)))
            
found.push(span.marker.parent || span.marker);
        }
        ++
lineNo;
      });
      return 
found;
    },
    
getAllMarks: function() {
      var 
markers = [];
      
this.iter(function(line) {
        var 
sps line.markedSpans;
        if (
sps) for (var 0sps.length; ++i)
          if (
sps[i].from != nullmarkers.push(sps[i].marker);
      });
      return 
markers;
    },

    
posFromIndex: function(off) {
      var 
chlineNo this.first;
      
this.iter(function(line) {
        var 
sz line.text.length 1;
        if (
sz off) { ch off; return true; }
        
off -= sz;
        ++
lineNo;
      });
      return 
clipPos(thisPos(lineNoch));
    },
    
indexFromPos: function (coords) {
      
coords clipPos(thiscoords);
      var 
index coords.ch;
      if (
coords.line this.first || coords.ch 0) return 0;
      
this.iter(this.firstcoords.line, function (line) {
        
index += line.text.length 1;
      });
      return 
index;
    },

    
copy: function(copyHistory) {
      var 
doc = new Doc(getLines(thisthis.firstthis.first this.size), this.modeOptionthis.first);
      
doc.scrollTop this.scrollTopdoc.scrollLeft this.scrollLeft;
      
doc.sel this.sel;
      
doc.extend false;
      if (
copyHistory) {
        
doc.history.undoDepth this.history.undoDepth;
        
doc.setHistory(this.getHistory());
      }
      return 
doc;
    },

    
linkedDoc: function(options) {
      if (!
optionsoptions = {};
      var 
from this.firstto this.first this.size;
      if (
options.from != null && options.from fromfrom options.from;
      if (
options.to != null && options.to toto options.to;
      var 
copy = new Doc(getLines(thisfromto), options.mode || this.modeOptionfrom);
      if (
options.sharedHistcopy.history this.history;
      (
this.linked || (this.linked = [])).push({doccopysharedHistoptions.sharedHist});
      
copy.linked = [{docthisisParenttruesharedHistoptions.sharedHist}];
      
copySharedMarkers(copyfindSharedMarkers(this));
      return 
copy;
    },
    
unlinkDoc: function(other) {
      if (
other instanceof CodeMirrorother other.doc;
      if (
this.linked) for (var 0this.linked.length; ++i) {
        var 
link this.linked[i];
        if (
link.doc != other) continue;
        
this.linked.splice(i1);
        
other.unlinkDoc(this);
        
detachSharedMarkers(findSharedMarkers(this));
        break;
      }
      
// If the histories were shared, split them again
      
if (other.history == this.history) {
        var 
splitIds = [other.id];
        
linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
        
other.history = new History(null);
        
other.history.done copyHistoryArray(this.history.donesplitIds);
        
other.history.undone copyHistoryArray(this.history.undonesplitIds);
      }
    },
    
iterLinkedDocs: function(f) {linkedDocs(thisf);},

    
getMode: function() {return this.mode;},
    
getEditor: function() {return this.cm;}
  });

  
// Public alias.
  
Doc.prototype.eachLine Doc.prototype.iter;

  
// Set up methods on CodeMirror's prototype to redirect to the editor's document.
  
var dontDelegate "iter insert remove copy getEditor".split(" ");
  for (var 
prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegateprop) < 0)
    
CodeMirror.prototype[prop] = (function(method) {
      return function() {return 
method.apply(this.docarguments);};
    })(
Doc.prototype[prop]);

  
eventMixin(Doc);

  
// Call f for all linked documents.
  
function linkedDocs(docfsharedHistOnly) {
    function 
propagate(docskipsharedHist) {
      if (
doc.linked) for (var 0doc.linked.length; ++i) {
        var 
rel doc.linked[i];
        if (
rel.doc == skip) continue;
        var 
shared sharedHist && rel.sharedHist;
        if (
sharedHistOnly && !shared) continue;
        
f(rel.docshared);
        
propagate(rel.docdocshared);
      }
    }
    
propagate(docnulltrue);
  }

  
// Attach a document to an editor.
  
function attachDoc(cmdoc) {
    if (
doc.cm) throw new Error("This document is already in use.");
    
cm.doc doc;
    
doc.cm cm;
    
estimateLineHeights(cm);
    
loadMode(cm);
    if (!
cm.options.lineWrappingfindMaxLine(cm);
    
cm.options.mode doc.modeOption;
    
regChange(cm);
  }

  
// LINE UTILITIES

  // Find the line object corresponding to the given line number.
  
function getLine(docn) {
    
-= doc.first;
    if (
|| >= doc.size) throw new Error("There is no line " + (doc.first) + " in the document.");
    for (var 
chunk doc; !chunk.lines;) {
      for (var 
0;; ++i) {
        var 
child chunk.children[i], sz child.chunkSize();
        if (
sz) { chunk child; break; }
        
-= sz;
      }
    }
    return 
chunk.lines[n];
  }

  
// Get the part of a document between two positions, as an array of
  // strings.
  
function getBetween(docstartend) {
    var 
out = [], start.line;
    
doc.iter(start.lineend.line 1, function(line) {
      var 
text line.text;
      if (
== end.linetext text.slice(0end.ch);
      if (
== start.linetext text.slice(start.ch);
      
out.push(text);
      ++
n;
    });
    return 
out;
  }
  
// Get the lines between from and to, as array of strings.
  
function getLines(docfromto) {
    var 
out = [];
    
doc.iter(fromto, function(line) { out.push(line.text); });
    return 
out;
  }

  
// Update the height of a line, propagating the height change
  // upwards to parent nodes.
  
function updateLineHeight(lineheight) {
    var 
diff height line.height;
    if (
diff) for (var linenn.parentn.height += diff;
  }

  
// Given a line object, find its line number by walking up through
  // its parent links.
  
function lineNo(line) {
    if (
line.parent == null) return null;
    var 
cur line.parentno indexOf(cur.linesline);
    for (var 
chunk cur.parentchunkcur chunkchunk chunk.parent) {
      for (var 
0;; ++i) {
        if (
chunk.children[i] == cur) break;
        
no += chunk.children[i].chunkSize();
      }
    }
    return 
no cur.first;
  }

  
// Find the line at the given vertical position, using the height
  // information in the document tree.
  
function lineAtHeight(chunkh) {
    var 
chunk.first;
    
outer: do {
      for (var 
0chunk.children.length; ++i) {
        var 
child chunk.children[i], ch child.height;
        if (
ch) { chunk child; continue outer; }
        
-= ch;
        
+= child.chunkSize();
      }
      return 
n;
    } while (!
chunk.lines);
    for (var 
0chunk.lines.length; ++i) {
      var 
line chunk.lines[i], lh line.height;
      if (
lh) break;
      
-= lh;
    }
    return 
i;
  }


  
// Find the height above the given line.
  
function heightAtLine(lineObj) {
    
lineObj visualLine(lineObj);

    var 
0chunk lineObj.parent;
    for (var 
0chunk.lines.length; ++i) {
      var 
line chunk.lines[i];
      if (
line == lineObj) break;
      else 
+= line.height;
    }
    for (var 
chunk.parentpchunk pchunk.parent) {
      for (var 
0p.children.length; ++i) {
        var 
cur p.children[i];
        if (
cur == chunk) break;
        else 
+= cur.height;
      }
    }
    return 
h;
  }

  
// Get the bidi ordering for the given line (and cache it). Returns
  // false for lines that are fully left-to-right, and an array of
  // BidiSpan objects otherwise.
  
function getOrder(line) {
    var 
order line.order;
    if (
order == nullorder line.order bidiOrdering(line.text);
    return 
order;
  }

  
// HISTORY

  
function History(startGen) {
    
// Arrays of change events and selections. Doing something adds an
    // event to done and clears undo. Undoing moves events from done
    // to undone, redoing moves them in the other direction.
    
this.done = []; this.undone = [];
    
this.undoDepth Infinity;
    
// Used to track when changes can be merged into a single undo
    // event
    
this.lastModTime this.lastSelTime 0;
    
this.lastOp null;
    
this.lastOrigin this.lastSelOrigin null;
    
// Used by the isClean() method
    
this.generation this.maxGeneration startGen || 1;
  }

  
// Create a history change event from an updateDoc-style change
  // object.
  
function historyChangeFromChange(docchange) {
    var 
histChange = {fromcopyPos(change.from), tochangeEnd(change), textgetBetween(docchange.fromchange.to)};
    
attachLocalSpans(dochistChangechange.from.linechange.to.line 1);
    
linkedDocs(doc, function(doc) {attachLocalSpans(dochistChangechange.from.linechange.to.line 1);}, true);
    return 
histChange;
  }

  
// Pop all selection events off the end of a history array. Stop at
  // a change event.
  
function clearSelectionEvents(array) {
    while (array.
length) {
      var 
last lst(array);
      if (
last.ranges) array.pop();
      else break;
    }
  }

  
// Find the top change event in the history. Pop off selection
  // events that are in the way.
  
function lastChangeEvent(histforce) {
    if (
force) {
      
clearSelectionEvents(hist.done);
      return 
lst(hist.done);
    } else if (
hist.done.length && !lst(hist.done).ranges) {
      return 
lst(hist.done);
    } else if (
hist.done.length && !hist.done[hist.done.length 2].ranges) {
      
hist.done.pop();
      return 
lst(hist.done);
    }
  }

  
// Register a change in the history. Merges changes that are within
  // a single operation, ore are close together with an origin that
  // allows merging (starting with "+") into a single event.
  
function addChangeToHistory(docchangeselAfteropId) {
    var 
hist doc.history;
    
hist.undone.length 0;
    var 
time = +new Datecur;

    if ((
hist.lastOp == opId ||
         
hist.lastOrigin == change.origin && change.origin &&
         ((
change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime time doc.cm.options.historyEventDelay) ||
          
change.origin.charAt(0) == "*")) &&
        (
cur lastChangeEvent(histhist.lastOp == opId))) {
      
// Merge this change into the last event
      
var last lst(cur.changes);
      if (
cmp(change.fromchange.to) == && cmp(change.fromlast.to) == 0) {
        
// Optimized case for simple insertion -- don't want to add
        // new changesets for every character typed
        
last.to changeEnd(change);
      } else {
        
// Add new sub-event
        
cur.changes.push(historyChangeFromChange(docchange));
      }
    } else {
      
// Can not be merged, start a new event.
      
var before lst(hist.done);
      if (!
before || !before.ranges)
        
pushSelectionToHistory(doc.selhist.done);
      
cur = {changes: [historyChangeFromChange(docchange)],
             
generationhist.generation};
      
hist.done.push(cur);
      while (
hist.done.length hist.undoDepth) {
        
hist.done.shift();
        if (!
hist.done[0].rangeshist.done.shift();
      }
    }
    
hist.done.push(selAfter);
    
hist.generation = ++hist.maxGeneration;
    
hist.lastModTime hist.lastSelTime time;
    
hist.lastOp opId;
    
hist.lastOrigin hist.lastSelOrigin change.origin;

    if (!
lastsignal(doc"historyAdded");
  }

  function 
selectionEventCanBeMerged(docoriginprevsel) {
    var 
ch origin.charAt(0);
    return 
ch == "*" ||
      
ch == "+" &&
      
prev.ranges.length == sel.ranges.length &&
      
prev.somethingSelected() == sel.somethingSelected() &&
      new 
Date doc.history.lastSelTime <= (doc.cm doc.cm.options.historyEventDelay 500);
  }

  
// Called whenever the selection changes, sets the new selection as
  // the pending selection in the history, and pushes the old pending
  // selection into the 'done' array when it was significantly
  // different (in number of selected ranges, emptiness, or time).
  
function addSelectionToHistory(docselopIdoptions) {
    var 
hist doc.historyorigin options && options.origin;

    
// A new event is started when the previous origin does not match
    // the current, or the origins don't allow matching. Origins
    // starting with * are always merged, those starting with + are
    // merged when similar and close together in time.
    
if (opId == hist.lastOp ||
        (
origin && hist.lastSelOrigin == origin &&
         (
hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
          
selectionEventCanBeMerged(docoriginlst(hist.done), sel))))
      
hist.done[hist.done.length 1] = sel;
    else
      
pushSelectionToHistory(selhist.done);

    
hist.lastSelTime = +new Date;
    
hist.lastSelOrigin origin;
    
hist.lastOp opId;
    if (
options && options.clearRedo !== false)
      
clearSelectionEvents(hist.undone);
  }

  function 
pushSelectionToHistory(seldest) {
    var 
top lst(dest);
    if (!(
top && top.ranges && top.equals(sel)))
      
dest.push(sel);
  }

  
// Used to store marked span information in the history.
  
function attachLocalSpans(docchangefromto) {
    var 
existing change["spans_" doc.id], 0;
    
doc.iter(Math.max(doc.firstfrom), Math.min(doc.first doc.sizeto), function(line) {
      if (
line.markedSpans)
        (
existing || (existing change["spans_" doc.id] = {}))[n] = line.markedSpans;
      ++
n;
    });
  }

  
// When un/re-doing restores text containing marked spans, those
  // that have been explicitly cleared should not be restored.
  
function removeClearedSpans(spans) {
    if (!
spans) return null;
    for (var 
0outspans.length; ++i) {
      if (
spans[i].marker.explicitlyCleared) { if (!outout spans.slice(0i); }
      else if (
outout.push(spans[i]);
    }
    return !
out spans out.length out null;
  }

  
// Retrieve and filter the old marked spans stored in a change event.
  
function getOldSpans(docchange) {
    var 
found change["spans_" doc.id];
    if (!
found) return null;
    for (var 
0nw = []; change.text.length; ++i)
      
nw.push(removeClearedSpans(found[i]));
    return 
nw;
  }

  
// Used both to provide a JSON-safe object in .getHistory, and, when
  // detaching a document, to split the history in two
  
function copyHistoryArray(eventsnewGroupinstantiateSel) {
    for (var 
0copy = []; events.length; ++i) {
      var 
event events[i];
      if (
event.ranges) {
        
copy.push(instantiateSel Selection.prototype.deepCopy.call(event) : event);
        continue;
      }
      var 
changes event.changesnewChanges = [];
      
copy.push({changesnewChanges});
      for (var 
0changes.length; ++j) {
        var 
change changes[j], m;
        
newChanges.push({fromchange.fromtochange.totextchange.text});
        if (
newGroup) for (var prop in change) if (prop.match(/^spans_(d+)$/)) {
          if (
indexOf(newGroupNumber(m[1])) > -1) {
            
lst(newChanges)[prop] = change[prop];
            
delete change[prop];
          }
        }
      }
    }
    return 
copy;
  }

  
// Rebasing/resetting history to deal with externally-sourced changes

  
function rebaseHistSelSingle(posfromtodiff) {
    if (
to pos.line) {
      
pos.line += diff;
    } else if (
from pos.line) {
      
pos.line from;
      
pos.ch 0;
    }
  }

  
// Tries to rebase an array of history events given a change in the
  // document. If the change touches the same lines as the event, the
  // event, and everything 'behind' it, is discarded. If the change is
  // before the event, the event's positions are updated. Uses a
  // copy-on-write scheme for the positions, to avoid having to
  // reallocate them all on every rebase, but also avoid problems with
  // shared position objects being unsafely updated.
  
function rebaseHistArray(array, fromtodiff) {
    for (var 
0< array.length; ++i) {
      var 
sub = array[i], ok true;
      if (
sub.ranges) {
        if (!
sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied true; }
        for (var 
0sub.ranges.lengthj++) {
          
rebaseHistSelSingle(sub.ranges[j].anchorfromtodiff);
          
rebaseHistSelSingle(sub.ranges[j].headfromtodiff);
        }
        continue;
      }
      for (var 
0sub.changes.length; ++j) {
        var 
cur sub.changes[j];
        if (
to cur.from.line) {
          
cur.from Pos(cur.from.line diffcur.from.ch);
          
cur.to Pos(cur.to.line diffcur.to.ch);
        } else if (
from <= cur.to.line) {
          
ok false;
          break;
        }
      }
      if (!
ok) {
        array.
splice(01);
        
0;
      }
    }
  }

  function 
rebaseHist(histchange) {
    var 
from change.from.lineto change.to.linediff change.text.length - (to from) - 1;
    
rebaseHistArray(hist.donefromtodiff);
    
rebaseHistArray(hist.undonefromtodiff);
  }

  
// EVENT UTILITIES

  // Due to the fact that we still support jurassic IE versions, some
  // compatibility wrappers are needed.

  
var e_preventDefault CodeMirror.e_preventDefault = function(e) {
    if (
e.preventDefaulte.preventDefault();
    else 
e.returnValue false;
  };
  var 
e_stopPropagation CodeMirror.e_stopPropagation = function(e) {
    if (
e.stopPropagatione.stopPropagation();
    else 
e.cancelBubble true;
  };
  function 
e_defaultPrevented(e) {
    return 
e.defaultPrevented != null e.defaultPrevented e.returnValue == false;
  }
  var 
e_stop CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};

  function 
e_target(e) {return e.target || e.srcElement;}
  function 
e_button(e) {
    var 
e.which;
    if (
== null) {
      if (
e.button 11;
      else if (
e.button 23;
      else if (
e.button 42;
    }
    if (
mac && e.ctrlKey && == 13;
    return 
b;
  }

  
// EVENT HANDLING

  // Lightweight event framework. on/off also work on DOM nodes,
  // registering native DOM handlers.

  
var on CodeMirror.on = function(emittertypef) {
    if (
emitter.addEventListener)
      
emitter.addEventListener(typeffalse);
    else if (
emitter.attachEvent)
      
emitter.attachEvent("on" typef);
    else {
      var 
map emitter._handlers || (emitter._handlers = {});
      var 
arr map[type] || (map[type] = []);
      
arr.push(f);
    }
  };

  var 
off CodeMirror.off = function(emittertypef) {
    if (
emitter.removeEventListener)
      
emitter.removeEventListener(typeffalse);
    else if (
emitter.detachEvent)
      
emitter.detachEvent("on" typef);
    else {
      var 
arr emitter._handlers && emitter._handlers[type];
      if (!
arr) return;
      for (var 
0arr.length; ++i)
        if (
arr[i] == f) { arr.splice(i1); break; }
    }
  };

  var 
signal CodeMirror.signal = function(emittertype /*, values...*/) {
    var 
arr emitter._handlers && emitter._handlers[type];
    if (!
arr) return;
    var 
args = Array.prototype.slice.call(arguments2);
    for (var 
0arr.length; ++iarr[i].apply(nullargs);
  };

  
// Often, we want to signal events at a point where we are in the
  // middle of some work, but don't want the handler to start calling
  // other methods on the editor, which might be in an inconsistent
  // state or simply not expect any other events to happen.
  // signalLater looks whether there are any handlers, and schedules
  // them to be executed when the last operation ends, or, if no
  // operation is active, when a timeout fires.
  
var delayedCallbacksdelayedCallbackDepth 0;
  function 
signalLater(emittertype /*, values...*/) {
    var 
arr emitter._handlers && emitter._handlers[type];
    if (!
arr) return;
    var 
args = Array.prototype.slice.call(arguments2);
    if (!
delayedCallbacks) {
      ++
delayedCallbackDepth;
      
delayedCallbacks = [];
      
setTimeout(fireDelayed0);
    }
    function 
bnd(f) {return function(){f.apply(nullargs);};};
    for (var 
0arr.length; ++i)
      
delayedCallbacks.push(bnd(arr[i]));
  }

  function 
fireDelayed() {
    --
delayedCallbackDepth;
    var 
delayed delayedCallbacks;
    
delayedCallbacks null;
    for (var 
0delayed.length; ++idelayed[i]();
  }

  
// The DOM events that CodeMirror handles can be overridden by
  // registering a (non-DOM) handler on the editor for the event name,
  // and preventDefault-ing the event in that handler.
  
function signalDOMEvent(cmeoverride) {
    
signal(cmoverride || e.typecme);
    return 
e_defaultPrevented(e) || e.codemirrorIgnore;
  }

  function 
signalCursorActivity(cm) {
    var 
arr cm._handlers && cm._handlers.cursorActivity;
    if (!
arr) return;
    var 
set cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
    for (var 
0arr.length; ++i) if (indexOf(setarr[i]) == -1)
      
set.push(arr[i]);
  }

  function 
hasHandler(emittertype) {
    var 
arr emitter._handlers && emitter._handlers[type];
    return 
arr && arr.length 0;
  }

  
// Add on and off methods to a constructor's prototype, to make
  // registering events on such objects more convenient.
  
function eventMixin(ctor) {
    
ctor.prototype.on = function(typef) {on(thistypef);};
    
ctor.prototype.off = function(typef) {off(thistypef);};
  }

  
// MISC UTILITIES

  // Number of pixels added to scroller and sizer to hide scrollbar
  
var scrollerCutOff 30;

  
// Returned or thrown by various protocols to signal 'I'm not
  // handling this'.
  
var Pass CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};

  
// Reused option objects for setSelection & friends
  
var sel_dontScroll = {scrollfalse}, sel_mouse = {origin"*mouse"}, sel_move = {origin"+move"};

  function 
Delayed() {this.id null;}
  
Delayed.prototype.set = function(msf) {
    
clearTimeout(this.id);
    
this.id setTimeout(fms);
  };

  
// Counts the column offset in a string, taking tabs into account.
  // Used mostly to find indentation.
  
var countColumn CodeMirror.countColumn = function(stringendtabSizestartIndexstartValue) {
    if (
end == null) {
      
end string.search(/[^su00a0]/);
      if (
end == -1end string.length;
    }
    for (var 
startIndex || 0startValue || 0;;) {
      var 
nextTab string.indexOf("t"i);
      if (
nextTab || nextTab >= end)
        return 
+ (end i);
      
+= nextTab i;
      
+= tabSize - (tabSize);
      
nextTab 1;
    }
  };

  
// The inverse of countColumn -- find the offset that corresponds to
  // a particular column.
  
function findColumn(stringgoaltabSize) {
    for (var 
pos 0col 0;;) {
      var 
nextTab string.indexOf("t"pos);
      if (
nextTab == -1nextTab string.length;
      var 
skipped nextTab pos;
      if (
nextTab == string.length || col skipped >= goal)
        return 
pos Math.min(skippedgoal col);
      
col += nextTab pos;
      
col += tabSize - (col tabSize);
      
pos nextTab 1;
      if (
col >= goal) return pos;
    }
  }

  var 
spaceStrs = [""];
  function 
spaceStr(n) {
    while (
spaceStrs.length <= n)
      
spaceStrs.push(lst(spaceStrs) + " ");
    return 
spaceStrs[n];
  }

  function 
lst(arr) { return arr[arr.length-1]; }

  var 
selectInput = function(node) { node.select(); };
  if (
ios// Mobile Safari apparently has a bug where select() is broken.
    
selectInput = function(node) { node.selectionStart 0node.selectionEnd node.value.length; };
  else if (
ie// Suppress mysterious IE10 errors
    
selectInput = function(node) { try { node.select(); } catch(_e) {} };

  function 
indexOf(array, elt) {
    for (var 
0< array.length; ++i)
      if (array[
i] == elt) return i;
    return -
1;
  }
  if ([].
indexOfindexOf = function(array, elt) { return array.indexOf(elt); };
  function 
map(array, f) {
    var 
out = [];
    for (var 
0< array.lengthi++) out[i] = f(array[i], i);
    return 
out;
  }
  if ([].
mapmap = function(array, f) { return array.map(f); };

  function 
createObj(baseprops) {
    var 
inst;
    if (
Object.create) {
      
inst Object.create(base);
    } else {
      var 
ctor = function() {};
      
ctor.prototype base;
      
inst = new ctor();
    }
    if (
propscopyObj(propsinst);
    return 
inst;
  };

  function 
copyObj(objtargetoverwrite) {
    if (!
targettarget = {};
    for (var 
prop in obj)
      if (
obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
        
target[prop] = obj[prop];
    return 
target;
  }

  function 
bind(f) {
    var 
args = Array.prototype.slice.call(arguments1);
    return function(){return 
f.apply(nullargs);};
  }

  var 
nonASCIISingleCaseWordChar = /[u00dfu3040-u309fu30a0-u30ffu3400-u4db5u4e00-u9fccuac00-ud7af]/;
  var 
isWordCharBasic CodeMirror.isWordChar = function(ch) {
    return /
w/.test(ch) || ch "x80" &&
      (
ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
  };
  function 
isWordChar(chhelper) {
    if (!
helper) return isWordCharBasic(ch);
    if (
helper.source.indexOf("\w") > -&& isWordCharBasic(ch)) return true;
    return 
helper.test(ch);
  }

  function 
isEmpty(obj) {
    for (var 
n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
    return 
true;
  }

  
// Extending unicode characters. A series of a non-extending char +
  // any number of extending chars is treated as a single unit as far
  // as editing and measuring is concerned. This is not fully correct,
  // since some scripts/fonts/browsers also treat other configurations
  // of code points as a group.
  
var extendingChars = /[u0300-u036fu0483-u0489u0591-u05bdu05bfu05c1u05c2u05c4u05c5u05c7u0610-u061au064b-u065eu0670u06d6-u06dcu06de-u06e4u06e7u06e8u06ea-u06edu0711u0730-u074au07a6-u07b0u07eb-u07f3u0816-u0819u081b-u0823u0825-u0827u0829-u082du0900-u0902u093cu0941-u0948u094du0951-u0955u0962u0963u0981u09bcu09beu09c1-u09c4u09cdu09d7u09e2u09e3u0a01u0a02u0a3cu0a41u0a42u0a47u0a48u0a4b-u0a4du0a51u0a70u0a71u0a75u0a81u0a82u0abcu0ac1-u0ac5u0ac7u0ac8u0acdu0ae2u0ae3u0b01u0b3cu0b3eu0b3fu0b41-u0b44u0b4du0b56u0b57u0b62u0b63u0b82u0bbeu0bc0u0bcdu0bd7u0c3e-u0c40u0c46-u0c48u0c4a-u0c4du0c55u0c56u0c62u0c63u0cbcu0cbfu0cc2u0cc6u0cccu0ccdu0cd5u0cd6u0ce2u0ce3u0d3eu0d41-u0d44u0d4du0d57u0d62u0d63u0dcau0dcfu0dd2-u0dd4u0dd6u0ddfu0e31u0e34-u0e3au0e47-u0e4eu0eb1u0eb4-u0eb9u0ebbu0ebcu0ec8-u0ecdu0f18u0f19u0f35u0f37u0f39u0f71-u0f7eu0f80-u0f84u0f86u0f87u0f90-u0f97u0f99-u0fbcu0fc6u102d-u1030u1032-u1037u1039u103au103du103eu1058u1059u105e-u1060u1071-u1074u1082u1085u1086u108du109du135fu1712-u1714u1732-u1734u1752u1753u1772u1773u17b7-u17bdu17c6u17c9-u17d3u17ddu180b-u180du18a9u1920-u1922u1927u1928u1932u1939-u193bu1a17u1a18u1a56u1a58-u1a5eu1a60u1a62u1a65-u1a6cu1a73-u1a7cu1a7fu1b00-u1b03u1b34u1b36-u1b3au1b3cu1b42u1b6b-u1b73u1b80u1b81u1ba2-u1ba5u1ba8u1ba9u1c2c-u1c33u1c36u1c37u1cd0-u1cd2u1cd4-u1ce0u1ce2-u1ce8u1cedu1dc0-u1de6u1dfd-u1dffu200cu200du20d0-u20f0u2cef-u2cf1u2de0-u2dffu302a-u302fu3099u309aua66f-ua672ua67cua67dua6f0ua6f1ua802ua806ua80bua825ua826ua8c4ua8e0-ua8f1ua926-ua92dua947-ua951ua980-ua982ua9b3ua9b6-ua9b9ua9bcuaa29-uaa2euaa31uaa32uaa35uaa36uaa43uaa4cuaab0uaab2-uaab4uaab7uaab8uaabeuaabfuaac1uabe5uabe8uabedudc00-udfffufb1eufe00-ufe0fufe20-ufe26uff9euff9f]/;
  function 
isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }

  
// DOM UTILITIES

  
function elt(tagcontentclassNamestyle) {
    var 
document.createElement(tag);
    if (
classNamee.className className;
    if (
stylee.style.cssText style;
    if (
typeof content == "string"e.appendChild(document.createTextNode(content));
    else if (
content) for (var 0content.length; ++ie.appendChild(content[i]);
    return 
e;
  }

  var 
range;
  if (
document.createRangerange = function(nodestartend) {
    var 
document.createRange();
    
r.setEnd(nodeend);
    
r.setStart(nodestart);
    return 
r;
  };
  else 
range = function(nodestartend) {
    var 
document.body.createTextRange();
    
r.moveToElementText(node.parentNode);
    
r.collapse(true);
    
r.moveEnd("character"end);
    
r.moveStart("character"start);
    return 
r;
  };

  function 
removeChildren(e) {
    for (var 
count e.childNodes.lengthcount 0; --count)
      
e.removeChild(e.firstChild);
    return 
e;
  }

  function 
removeChildrenAndAdd(parente) {
    return 
removeChildren(parent).appendChild(e);
  }

  function 
contains(parentchild) {
    if (
parent.contains)
      return 
parent.contains(child);
    while (
child child.parentNode)
      if (
child == parent) return true;
  }

  function 
activeElt() { return document.activeElement; }
  
// Older versions of IE throws unspecified error when touching
  // document.activeElement in some cases (during loading, in iframe)
  
if (ie_upto10activeElt = function() {
    try { return 
document.activeElement; }
    catch(
e) { return document.body; }
  };

  function 
classTest(cls) { return new RegExp("\b" cls "\b\s*"); }
  function 
rmClass(nodecls) {
    var 
test classTest(cls);
    if (
test.test(node.className)) node.className node.className.replace(test"");
  }
  function 
addClass(nodecls) {
    if (!
classTest(cls).test(node.className)) node.className += " " cls;
  }
  function 
joinClasses(ab) {
    var as = 
a.split(" ");
    for (var 
0< as.lengthi++)
      if (as[
i] && !classTest(as[i]).test(b)) += " " + as[i];
    return 
b;
  }

  
// WINDOW-WIDE EVENTS

  // These must be handled carefully, because naively registering a
  // handler for each editor will cause the editors to never be
  // garbage collected.

  
function forEachCodeMirror(f) {
    if (!
document.body.getElementsByClassName) return;
    var 
byClass document.body.getElementsByClassName("CodeMirror");
    for (var 
0byClass.lengthi++) {
      var 
cm byClass[i].CodeMirror;
      if (
cmf(cm);
    }
  }

  var 
globalsRegistered false;
  function 
ensureGlobalHandlers() {
    if (
globalsRegistered) return;
    
registerGlobalHandlers();
    
globalsRegistered true;
  }
  function 
registerGlobalHandlers() {
    
// When the window resizes, we need to refresh active editors.
    
var resizeTimer;
    
on(window"resize", function() {
      if (
resizeTimer == nullresizeTimer setTimeout(function() {
        
resizeTimer null;
        
knownScrollbarWidth null;
        
forEachCodeMirror(onResize);
      }, 
100);
    });
    
// When the window loses focus, we want to show the editor as blurred
    
on(window"blur", function() {
      
forEachCodeMirror(onBlur);
    });
  }

  
// FEATURE DETECTION

  // Detect drag-and-drop
  
var dragAndDrop = function() {
    
// There is *some* kind of drag-and-drop support in IE6-8, but I
    // couldn't get it to work yet.
    
if (ie_upto8) return false;
    var 
div elt('div');
    return 
"draggable" in div || "dragDrop" in div;
  }();

  var 
knownScrollbarWidth;
  function 
scrollbarWidth(measure) {
    if (
knownScrollbarWidth != null) return knownScrollbarWidth;
    var 
test elt("div"nullnull"width: 50px; height: 50px; overflow-x: scroll");
    
removeChildrenAndAdd(measuretest);
    if (
test.offsetWidth)
      
knownScrollbarWidth test.offsetHeight test.clientHeight;
    return 
knownScrollbarWidth || 0;
  }

  var 
zwspSupported;
  function 
zeroWidthElement(measure) {
    if (
zwspSupported == null) {
      var 
test elt("span""u200b");
      
removeChildrenAndAdd(measureelt("span", [testdocument.createTextNode("x")]));
      if (
measure.firstChild.offsetHeight != 0)
        
zwspSupported test.offsetWidth <= && test.offsetHeight && !ie_upto7;
    }
    if (
zwspSupported) return elt("span""u200b");
    else return 
elt("span""u00a0"null"display: inline-block; width: 1px; margin-right: -1px");
  }

  
// Feature-detect IE's crummy client rect reporting for bidi text
  
var badBidiRects;
  function 
hasBadBidiRects(measure) {
    if (
badBidiRects != null) return badBidiRects;
    var 
txt removeChildrenAndAdd(measuredocument.createTextNode("Au062eA"));
    var 
r0 range(txt01).getBoundingClientRect();
    if (
r0.left == r0.right) return false;
    var 
r1 range(txt12).getBoundingClientRect();
    return 
badBidiRects = (r1.right r0.right 3);
  }

  
// See if "".split is the broken IE version, if so, provide an
  // alternative way to split lines.
  
var splitLines CodeMirror.splitLines "nnb".split(/n/).length != ? function(string) {
    var 
pos 0result = [], string.length;
    while (
pos <= l) {
      var 
nl string.indexOf("n"pos);
      if (
nl == -1nl string.length;
      var 
line string.slice(posstring.charAt(nl 1) == "r" nl nl);
      var 
rt line.indexOf("r");
      if (
rt != -1) {
        
result.push(line.slice(0rt));
        
pos += rt 1;
      } else {
        
result.push(line);
        
pos nl 1;
      }
    }
    return 
result;
  } : function(string){return 
string.split(/rn?|n/);};

  var 
hasSelection window.getSelection ? function(te) {
    try { return 
te.selectionStart != te.selectionEnd; }
    catch(
e) { return false; }
  } : function(
te) {
    try {var 
range te.ownerDocument.selection.createRange();}
    catch(
e) {}
    if (!
range || range.parentElement() != te) return false;
    return 
range.compareEndPoints("StartToEnd"range) != 0;
  };

  var 
hasCopyEvent = (function() {
    var 
elt("div");
    if (
"oncopy" in e) return true;
    
e.setAttribute("oncopy""return;");
    return 
typeof e.oncopy == "function";
  })();

  
// KEY NAMES

  
var keyNames = {3"Enter"8"Backspace"9"Tab"13"Enter"16"Shift"17"Ctrl"18"Alt",
                  
19"Pause"20"CapsLock"27"Esc"32"Space"33"PageUp"34"PageDown"35"End",
                  
36"Home"37"Left"38"Up"39"Right"40"Down"44"PrintScrn"45"Insert",
                  
46"Delete"59";"61"="91"Mod"92"Mod"93"Mod"107"="109"-"127"Delete",
                  
173"-"186";"187"="188","189"-"190"."191"/"192"`"219"["220"\",
                  221: "
]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
                  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
  CodeMirror.keyNames = keyNames;
  (function() {
    // Number keys
    for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
    // Alphabetic keys
    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
    // Function keys
    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
  })();

  // BIDI HELPERS

  function iterateBidiSections(order, from, to, f) {
    if (!order) return f(from, to, "ltr");
    var found = false;
    for (var i = 0; i < order.length; ++i) {
      var part = order[i];
      if (part.from < to && part.to > from || from == to && part.to == from) {
        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
        found = true;
      }
    }
    if (!found) f(from, to, "ltr");
  }

  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }

  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
  function lineRight(line) {
    var order = getOrder(line);
    if (!order) return line.text.length;
    return bidiRight(lst(order));
  }

  function lineStart(cm, lineN) {
    var line = getLine(cm.doc, lineN);
    var visual = visualLine(line);
    if (visual != line) lineN = lineNo(visual);
    var order = getOrder(visual);
    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
    return Pos(lineN, ch);
  }
  function lineEnd(cm, lineN) {
    var merged, line = getLine(cm.doc, lineN);
    while (merged = collapsedSpanAtEnd(line)) {
      line = merged.find(1, true).line;
      lineN = null;
    }
    var order = getOrder(line);
    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
    return Pos(lineN == null ? lineNo(line) : lineN, ch);
  }

  function compareBidiLevel(order, a, b) {
    var linedir = order[0].level;
    if (a == linedir) return true;
    if (b == linedir) return false;
    return a < b;
  }
  var bidiOther;
  function getBidiPartAt(order, pos) {
    bidiOther = null;
    for (var i = 0, found; i < order.length; ++i) {
      var cur = order[i];
      if (cur.from < pos && cur.to > pos) return i;
      if ((cur.from == pos || cur.to == pos)) {
        if (found == null) {
          found = i;
        } else if (compareBidiLevel(order, cur.level, order[found].level)) {
          if (cur.from != cur.to) bidiOther = found;
          return i;
        } else {
          if (cur.from != cur.to) bidiOther = i;
          return found;
        }
      }
    }
    return found;
  }

  function moveInLine(line, pos, dir, byUnit) {
    if (!byUnit) return pos + dir;
    do pos += dir;
    while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
    return pos;
  }

  // This is needed in order to move '
visually' through bi-directional
  // text -- i.e., pressing left should make the cursor go left, even
  // when in RTL text. The tricky part is the '
jumps', where RTL and
  // LTR text touch each other. This often requires the cursor offset
  // to move more than one unit, in order to visually move one unit.
  function moveVisually(line, start, dir, byUnit) {
    var bidi = getOrder(line);
    if (!bidi) return moveLogically(line, start, dir, byUnit);
    var pos = getBidiPartAt(bidi, start), part = bidi[pos];
    var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);

    for (;;) {
      if (target > part.from && target < part.to) return target;
      if (target == part.from || target == part.to) {
        if (getBidiPartAt(bidi, target) == pos) return target;
        part = bidi[pos += dir];
        return (dir > 0) == part.level % 2 ? part.to : part.from;
      } else {
        part = bidi[pos += dir];
        if (!part) return null;
        if ((dir > 0) == part.level % 2)
          target = moveInLine(line, part.to, -1, byUnit);
        else
          target = moveInLine(line, part.from, 1, byUnit);
      }
    }
  }

  function moveLogically(line, start, dir, byUnit) {
    var target = start + dir;
    if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
    return target < 0 || target > line.text.length ? null : target;
  }

  // Bidirectional ordering algorithm
  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
  // that this (partially) implements.

  // One-char codes used for character types:
  // L (L):   Left-to-Right
  // R (R):   Right-to-Left
  // r (AL):  Right-to-Left Arabic
  // 1 (EN):  European Number
  // + (ES):  European Number Separator
  // % (ET):  European Number Terminator
  // n (AN):  Arabic Number
  // , (CS):  Common Number Separator
  // m (NSM): Non-Spacing Mark
  // b (BN):  Boundary Neutral
  // s (B):   Paragraph Separator
  // t (S):   Segment Separator
  // w (WS):  Whitespace
  // N (ON):  Other Neutrals

  // Returns null if characters are ordered as they appear
  // (left-to-right), or an array of sections ({from, to, level}
  // objects) in the order in which they occur visually.
  var bidiOrdering = (function() {
    // Character types for codepoints 0 to 0xff
    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
    // Character types for codepoints 0x600 to 0x6ff
    var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
    function charType(code) {
      if (code <= 0xf7) return lowTypes.charAt(code);
      else if (0x590 <= code && code <= 0x5f4) return "R";
      else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
      else if (0x6ee <= code && code <= 0x8ac) return "r";
      else if (0x2000 <= code && code <= 0x200b) return "w";
      else if (code == 0x200c) return "b";
      else return "L";
    }

    var bidiRE = /[u0590-u05f4u0600-u06ffu0700-u08ac]/;
    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
    // Browsers seem to always treat the boundaries of block elements as being L.
    var outerType = "L";

    function BidiSpan(level, from, to) {
      this.level = level;
      this.from = from; this.to = to;
    }

    return function(str) {
      if (!bidiRE.test(str)) return false;
      var len = str.length, types = [];
      for (var i = 0, type; i < len; ++i)
        types.push(type = charType(str.charCodeAt(i)));

      // W1. Examine each non-spacing mark (NSM) in the level run, and
      // change the type of the NSM to the type of the previous
      // character. If the NSM is at the start of the level run, it will
      // get the type of sor.
      for (var i = 0, prev = outerType; i < len; ++i) {
        var type = types[i];
        if (type == "m") types[i] = prev;
        else prev = type;
      }

      // W2. Search backwards from each instance of a European number
      // until the first strong type (R, L, AL, or sor) is found. If an
      // AL is found, change the type of the European number to Arabic
      // number.
      // W3. Change all ALs to R.
      for (var i = 0, cur = outerType; i < len; ++i) {
        var type = types[i];
        if (type == "1" && cur == "r") types[i] = "n";
        else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
      }

      // W4. A single European separator between two European numbers
      // changes to a European number. A single common separator between
      // two numbers of the same type changes to that type.
      for (var i = 1, prev = types[0]; i < len - 1; ++i) {
        var type = types[i];
        if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
        else if (type == "," && prev == types[i+1] &&
                 (prev == "1" || prev == "n")) types[i] = prev;
        prev = type;
      }

      // W5. A sequence of European terminators adjacent to European
      // numbers changes to all European numbers.
      // W6. Otherwise, separators and terminators change to Other
      // Neutral.
      for (var i = 0; i < len; ++i) {
        var type = types[i];
        if (type == ",") types[i] = "N";
        else if (type == "%") {
          for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
          var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
          for (var j = i; j < end; ++j) types[j] = replace;
          i = end - 1;
        }
      }

      // W7. Search backwards from each instance of a European number
      // until the first strong type (R, L, or sor) is found. If an L is
      // found, then change the type of the European number to L.
      for (var i = 0, cur = outerType; i < len; ++i) {
        var type = types[i];
        if (cur == "L" && type == "1") types[i] = "L";
        else if (isStrong.test(type)) cur = type;
      }

      // N1. A sequence of neutrals takes the direction of the
      // surrounding strong text if the text on both sides has the same
      // direction. European and Arabic numbers act as if they were R in
      // terms of their influence on neutrals. Start-of-level-run (sor)
      // and end-of-level-run (eor) are used at level run boundaries.
      // N2. Any remaining neutrals take the embedding direction.
      for (var i = 0; i < len; ++i) {
        if (isNeutral.test(types[i])) {
          for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
          var before = (i ? types[i-1] : outerType) == "L";
          var after = (end < len ? types[end] : outerType) == "L";
          var replace = before || after ? "L" : "R";
          for (var j = i; j < end; ++j) types[j] = replace;
          i = end - 1;
        }
      }

      // Here we depart from the documented algorithm, in order to avoid
      // building up an actual levels array. Since there are only three
      // levels (0, 1, 2) in an implementation that doesn'
t take
      
// explicit embedding into account, we can build up the order on
      // the fly, without following the level-based algorithm.
      
var order = [], m;
      for (var 
0len;) {
        if (
countsAsLeft.test(types[i])) {
          var 
start i;
          for (++
ilen && countsAsLeft.test(types[i]); ++i) {}
          
order.push(new BidiSpan(0starti));
        } else {
          var 
pos iat order.length;
          for (++
ilen && types[i] != "L"; ++i) {}
          for (var 
posi;) {
            if (
countsAsNum.test(types[j])) {
              if (
pos jorder.splice(at0, new BidiSpan(1posj));
              var 
nstart j;
              for (++
j&& countsAsNum.test(types[j]); ++j) {}
              
order.splice(at0, new BidiSpan(2nstartj));
              
pos j;
            } else ++
j;
          }
          if (
pos iorder.splice(at0, new BidiSpan(1posi));
        }
      }
      if (
order[0].level == && (str.match(/^s+/))) {
        
order[0].from m[0].length;
        
order.unshift(new BidiSpan(00m[0].length));
      }
      if (
lst(order).level == && (str.match(/s+$/))) {
        
lst(order).to -= m[0].length;
        
order.push(new BidiSpan(0len m[0].lengthlen));
      }
      if (
order[0].level != lst(order).level)
        
order.push(new BidiSpan(order[0].levellenlen));

      return 
order;
    };
  })();

  
// THE END

  
CodeMirror.version "4.2.0";

  return 
CodeMirror;
});
?>
Онлайн: 1
Реклама