Вход Регистрация
Файл: mg-core/script/codemirror/lib/codemirror.js
Строк: 9491
<?php
// CodeMirror version 3.16
//
// CodeMirror is the only global var we claim
window.CodeMirror = (function() {
  
"use strict";

  
// BROWSER SNIFFING

  // Crude, but necessary to handle a number of hard-to-feature-detect
  // bugs and behavior differences.
  
var gecko = /gecko/d/i.test(navigator.userAgent);
  var 
ie = /MSIE d/.test(navigator.userAgent);
  var 
ie_lt8 ie && (document.documentMode == null || document.documentMode 8);
  var 
ie_lt9 ie && (document.documentMode == null || document.documentMode 9);
  var 
webkit = /WebKit//.test(navigator.userAgent);
  
var qtwebkit webkit && /Qt/d+.d+/.test(navigator.userAgent);
  var 
chrome = /Chrome//.test(navigator.userAgent);
  
var opera = /Opera//.test(navigator.userAgent);
  
var safari = /Apple Computer/.test(navigator.vendor);
  var 
khtml = /KHTML//.test(navigator.userAgent);
  
var mac_geLion = /Mac OS X 1dD([7-9]|dd)D/.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 
opera_version opera && navigator.userAgent.match(/Version/(d*.d*)/);
  if (
opera_versionopera_version Number(opera_version[1]);
  if (
opera_version && opera_version >= 15) { opera falsewebkit true; }
  
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
  
var flipCtrlCmd mac && (qtwebkit || opera && (opera_version == null || opera_version 12.11));
  var 
captureMiddleClick gecko || (ie && !ie_lt9);

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

  
// CONSTRUCTOR

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

    
this.options options options || {};
    
// Determine effective options based on given values and defaults.
    
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
      
options[opt] = defaults[opt];
    
setGuttersForLineNumbers(options);

    var 
docStart typeof options.value == "string" options.value.first;
    var 
display this.display makeDisplay(placedocStart);
    
display.wrapper.CodeMirror this;
    
updateGutters(this);
    if (
options.autofocus && !mobilefocusInput(this);

    
this.state = {keyMaps: [],
                  
overlays: [],
                  
modeGen0,
                  
overwritefalsefocusedfalse,
                  
suppressEditsfalsepasteIncomingfalse,
                  
draggingTextfalse,
                  
highlight: new Delayed()};

    
themeChanged(this);
    if (
options.lineWrapping)
      
this.display.wrapper.className += " CodeMirror-wrap";

    var 
doc options.value;
    if (
typeof doc == "string"doc = new Doc(options.valueoptions.mode);
    
operation(thisattachDoc)(thisdoc);

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

    
registerEventHandlers(this);
    
// IE throws unspecified error in certain cases, when
    // trying to access activeElement before onload
    
var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
    if (
hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocusthis), 20);
    else 
onBlur(this);

    
operation(this, function() {
      for (var 
opt in optionHandlers)
        if (
optionHandlers.propertyIsEnumerable(opt))
          
optionHandlers[opt](thisoptions[opt], Init);
      for (var 
0initHooks.length; ++iinitHooks[i](this);
    })();
  }

  
// DISPLAY CONSTRUCTOR

  
function makeDisplay(placedocStart) {
    var 
= {};

    var 
input d.input elt("textarea"nullnull"position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
    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 actual fake scrollbars.
    
d.scrollbarH elt("div", [elt("div"nullnull"height: 1px")], "CodeMirror-hscrollbar");
    
d.scrollbarV elt("div", [elt("div"nullnull"width: 1px")], "CodeMirror-vscrollbar");
    
d.scrollbarFiller elt("div"null"CodeMirror-scrollbar-filler");
    
d.gutterFiller elt("div"null"CodeMirror-gutter-filler");
    
// DIVs containing the selection and the actual code
    
d.lineDiv elt("div"null"CodeMirror-code");
    
d.selectionDiv elt("div"nullnull"position: relative; z-index: 1");
    
// Blinky cursor, and element used to ensure cursor fits at the end of a line
    
d.cursor elt("div""u00a0""CodeMirror-cursor");
    
// Secondary cursor, shown when on a 'jump' in bi-directional text
    
d.otherCursor elt("div""u00a0""CodeMirror-cursor CodeMirror-secondarycursor");
    
// Used to measure text size
    
d.measure elt("div"null"CodeMirror-measure");
    
// Wraps everything that needs to exist inside the vertically-padded coordinate system
    
d.lineSpace elt("div", [d.measured.selectionDivd.lineDivd.cursord.otherCursor],
                         
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 text, causes scrolling
    
d.sizer elt("div", [d.mover], "CodeMirror-sizer");
    
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
    
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;
    
// Provides scrolling
    
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
    
if (ie_lt8) { d.gutters.style.zIndex = -1d.scroller.style.paddingRight 0; }
    if (
place.appendChildplace.appendChild(d.wrapper); else place(d.wrapper);

    
// 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).
    
else if (ie_lt8d.scrollbarH.style.minWidth d.scrollbarV.style.minWidth "18px";

    
// Current visible range (may be bigger than the view window).
    
d.viewOffset d.lastSizeC 0;
    
d.showingFrom d.showingTo docStart;

    
// 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 widget is added. As
    // an optimization, widget aligning is skipped when d is false.
    
d.alignWidgets false;
    
// Flag that indicates whether we currently expect input to appear
    // (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 null;
    
d.measureLineCache = [];
    
d.measureLineCachePos 0;

    
// Tracks when resetInput has punted to just putting a short
    // string instead of the (large) 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;

    return 
d;
  }

  
// 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);
    
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) {
      
cm.display.wrapper.className += " CodeMirror-wrap";
      
cm.display.sizer.style.minWidth "";
    } else {
      
cm.display.wrapper.className cm.display.wrapper.className.replace(" CodeMirror-wrap""");
      
computeMaxLength(cm);
    }
    
estimateLineHeights(cm);
    
regChange(cm);
    
clearCaches(cm);
    
setTimeout(function(){updateScrollbars(cm);}, 100);
  }

  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;
      else if (
wrapping)
        return (
Math.ceil(line.text.length perLine) || 1) * th;
      else
        return 
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 "");
    
cm.state.disableInput map.disableInput;
  }

  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);
  }

  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";
  }

  function 
lineLength(docline) {
    if (
line.height == 0) return 0;
    var 
len line.text.lengthmergedcur line;
    while (
merged collapsedSpanAtStart(cur)) {
      var 
found merged.find();
      
cur getLine(docfound.from.line);
      
len += found.from.ch found.to.ch;
    }
    
cur line;
    while (
merged collapsedSpanAtEnd(cur)) {
      var 
found merged.find();
      
len -= cur.text.length found.from.ch;
      
cur getLine(docfound.to.line);
      
len += cur.text.length found.to.ch;
    }
    return 
len;
  }

  function 
computeMaxLength(cm) {
    var 
cm.displaydoc cm.doc;
    
d.maxLine getLine(docdoc.first);
    
d.maxLineLength lineLength(docd.maxLine);
    
d.maxLineChanged true;
    
doc.iter(function(line) {
      var 
len lineLength(docline);
      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 false;
    for (var 
0options.gutters.length; ++i) {
      if (
options.gutters[i] == "CodeMirror-linenumbers") {
        if (
options.lineNumbersfound true;
        else 
options.gutters.splice(i--, 1);
      }
    }
    if (!
found && options.lineNumbers)
      
options.gutters.push("CodeMirror-linenumbers");
  }

  
// SCROLLBARS

  // Re-synchronize the fake scrollbars with the actual size of the
  // content. Optionally force a scrollTop.
  
function updateScrollbars(cm) {
    var 
cm.displaydocHeight cm.doc.height;
    var 
totalHeight docHeight paddingVert(d);
    
d.sizer.style.minHeight d.heightForcer.style.top totalHeight "px";
    
d.gutters.style.height Math.max(totalHeightd.scroller.clientHeight scrollerCutOff) + "px";
    var 
scrollHeight Math.max(totalHeightd.scroller.scrollHeight);
    var 
needsH d.scroller.scrollWidth > (d.scroller.clientWidth 1);
    var 
needsV scrollHeight > (d.scroller.clientHeight 1);
    if (
needsV) {
      
d.scrollbarV.style.display "block";
      
d.scrollbarV.style.bottom needsH scrollbarWidth(d.measure) + "px" "0";
      
d.scrollbarV.firstChild.style.height =
        (
scrollHeight d.scroller.clientHeight 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 =
        (
d.scroller.scrollWidth d.scroller.clientWidth 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 (
mac_geLion && scrollbarWidth(d.measure) === 0)
      
d.scrollbarV.style.minWidth d.scrollbarH.style.minHeight mac_geMountainLion "18px" "12px";
  }

  function 
visibleLines(displaydocviewPort) {
    var 
top display.scroller.scrollTopheight display.wrapper.clientHeight;
    if (
typeof viewPort == "number"top viewPort;
    else if (
viewPort) {top viewPort.topheight viewPort.bottom viewPort.top;}
    
top Math.floor(top paddingTop(display));
    var 
bottom Math.ceil(top height);
    return {
fromlineAtHeight(doctop), tolineAtHeight(docbottom)};
  }

  
// LINE NUMBERS

  
function alignHorizontally(cm) {
    var 
display cm.display;
    if (!
display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
    var 
comp compensateForHScroll(display) - display.scroller.scrollLeft cm.doc.scrollLeft;
    var 
gutterW display.gutters.offsetWidthcomp "px";
    for (var 
display.lineDiv.firstChildnn.nextSibling) if (n.alignable) {
      for (var 
0n.alignablea.length; ++ia[i].style.left l;
    }
    if (
cm.options.fixedGutter)
      
display.gutters.style.left = (comp gutterW) + "px";
  }

  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";
      return 
true;
    }
    return 
false;
  }

  function 
lineNumberFor(optionsi) {
    return 
String(options.lineNumberFormatter(options.firstLineNumber));
  }
  function 
compensateForHScroll(display) {
    return 
getRect(display.scroller).left getRect(display.sizer).left;
  }

  
// DISPLAY DRAWING

  
function updateDisplay(cmchangesviewPortforced) {
    var 
oldFrom cm.display.showingFromoldTo cm.display.showingToupdated;
    var 
visible visibleLines(cm.displaycm.docviewPort);
    for (;;) {
      if (!
updateDisplayInner(cmchangesvisibleforced)) break;
      
forced false;
      
updated true;
      
updateSelection(cm);
      
updateScrollbars(cm);

      
// Clip forced viewport to actual scrollable area
      
if (viewPort)
        
viewPort Math.min(cm.display.scroller.scrollHeight cm.display.scroller.clientHeight,
                            
typeof viewPort == "number" viewPort viewPort.top);
      
visible visibleLines(cm.displaycm.docviewPort);
      if (
visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
        break;
      
changes = [];
    }

    if (
updated) {
      
signalLater(cm"update"cm);
      if (
cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
        
signalLater(cm"viewportChange"cmcm.display.showingFromcm.display.showingTo);
    }
    return 
updated;
  }

  
// Uses a set of changes plus the current scroll position to
  // determine which DOM updates have to be made, and makes the
  // updates.
  
function updateDisplayInner(cmchangesvisibleforced) {
    var 
display cm.displaydoc cm.doc;
    if (!
display.wrapper.clientWidth) {
      
display.showingFrom display.showingTo doc.first;
      
display.viewOffset 0;
      return;
    }

    
// Bail out if the visible area is already rendered and nothing changed.
    
if (!forced && changes.length == &&
        
visible.from display.showingFrom && visible.to display.showingTo)
      return;

    if (
maybeUpdateLineNumberWidth(cm))
      
changes = [{fromdoc.firsttodoc.first doc.size}];
    var 
gutterW display.sizer.style.marginLeft display.gutters.offsetWidth "px";
    
display.scrollbarH.style.left cm.options.fixedGutter gutterW "0";

    
// Used to determine which lines need their line numbers updated
    
var positionsChangedFrom Infinity;
    if (
cm.options.lineNumbers)
      for (var 
0changes.length; ++i)
        if (
changes[i].diff && changes[i].from positionsChangedFrom) { positionsChangedFrom changes[i].from; }

    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.showingFrom from && from display.showingFrom 20from Math.max(doc.firstdisplay.showingFrom);
    if (
display.showingTo to && display.showingTo to 20to Math.min(enddisplay.showingTo);
    if (
sawCollapsedSpans) {
      
from lineNo(visualLine(docgetLine(docfrom)));
      while (
to end && lineIsHidden(docgetLine(docto))) ++to;
    }

    
// Create a range of theoretically intact lines, and punch holes
    // in that using the change info.
    
var intact = [{fromMath.max(display.showingFromdoc.first),
                   
toMath.min(display.showingToend)}];
    if (
intact[0].from >= intact[0].tointact = [];
    else 
intact computeIntact(intactchanges);
    
// When merged lines are present, we might have to reduce the
    // intact ranges because changes in continued fragments of the
    // intact lines do require the lines to be redrawn.
    
if (sawCollapsedSpans)
      for (var 
0intact.length; ++i) {
        var 
range intact[i], merged;
        while (
merged collapsedSpanAtEnd(getLine(docrange.to 1))) {
          var 
newTo merged.find().from.line;
          if (
newTo range.fromrange.to newTo;
          else { 
intact.splice(i--, 1); break; }
        }
      }

    
// Clip off the parts that won't be visible
    
var intactLines 0;
    for (var 
0intact.length; ++i) {
      var 
range intact[i];
      if (
range.from fromrange.from from;
      if (
range.to torange.to to;
      if (
range.from >= range.tointact.splice(i--, 1);
      else 
intactLines += range.to range.from;
    }
    if (!
forced && intactLines == to from && from == display.showingFrom && to == display.showingTo) {
      
updateViewOffset(cm);
      return;
    }
    
intact.sort(function(ab) {return a.from b.from;});

    
// Avoid crashing on IE's "unspecified error" when in iframes
    
try {
      var 
focused document.activeElement;
    } catch(
e) {}
    if (
intactLines < (to from) * .7display.lineDiv.style.display "none";
    
patchDisplay(cmfromtointactpositionsChangedFrom);
    
display.lineDiv.style.display "";
    if (
focused && document.activeElement != focused && focused.offsetHeightfocused.focus();

    var 
different from != display.showingFrom || to != display.showingTo ||
      
display.lastSizeC != display.wrapper.clientHeight;
    
// This is just a bogus formula that detects when the editor is
    // resized or the font size changes.
    
if (different) {
      
display.lastSizeC display.wrapper.clientHeight;
      
startWorker(cm400);
    }
    
display.showingFrom fromdisplay.showingTo to;

    
updateHeightsInViewport(cm);
    
updateViewOffset(cm);

    return 
true;
  }

  function 
updateHeightsInViewport(cm) {
    var 
display cm.display;
    var 
prevBottom display.lineDiv.offsetTop;
    for (var 
node display.lineDiv.firstChildheightnodenode node.nextSibling) if (node.lineObj) {
      if (
ie_lt8) {
        var 
bot node.offsetTop node.offsetHeight;
        
height bot prevBottom;
        
prevBottom bot;
      } else {
        var 
box getRect(node);
        
height box.bottom box.top;
      }
      var 
diff node.lineObj.height height;
      if (
height 2height textHeight(display);
      if (
diff .001 || diff < -.001) {
        
updateLineHeight(node.lineObjheight);
        var 
widgets node.lineObj.widgets;
        if (
widgets) for (var 0widgets.length; ++i)
          
widgets[i].height widgets[i].node.offsetHeight;
      }
    }
  }

  function 
updateViewOffset(cm) {
    var 
off cm.display.viewOffset heightAtLine(cmgetLine(cm.doccm.display.showingFrom));
    
// Position the mover div to align with the current virtual scroll position
    
cm.display.mover.style.top off "px";
  }

  function 
computeIntact(intactchanges) {
    for (var 
0changes.length || 0l; ++i) {
      var 
change changes[i], intact2 = [], diff change.diff || 0;
      for (var 
0l2 intact.lengthl2; ++j) {
        var 
range intact[j];
        if (
change.to <= range.from && change.diff) {
          
intact2.push({fromrange.from difftorange.to diff});
        } else if (
change.to <= range.from || change.from >= range.to) {
          
intact2.push(range);
        } else {
          if (
change.from range.from)
            
intact2.push({fromrange.fromtochange.from});
          if (
change.to range.to)
            
intact2.push({fromchange.to difftorange.to diff});
        }
      }
      
intact intact2;
    }
    return 
intact;
  }

  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};
  }

  function 
patchDisplay(cmfromtointactupdateNumbersFrom) {
    var 
dims getDimensions(cm);
    var 
display cm.displaylineNumbers cm.options.lineNumbers;
    if (!
intact.length && (!webkit || !cm.display.currentWheelTarget))
      
removeChildren(display.lineDiv);
    var 
container display.lineDivcur container.firstChild;

    function 
rm(node) {
      var 
next node.nextSibling;
      if (
webkit && mac && cm.display.currentWheelTarget == node) {
        
node.style.display "none";
        
node.lineObj null;
      } else {
        
node.parentNode.removeChild(node);
      }
      return 
next;
    }

    var 
nextIntact intact.shift(), lineN from;
    
cm.doc.iter(fromto, function(line) {
      if (
nextIntact && nextIntact.to == lineNnextIntact intact.shift();
      if (
lineIsHidden(cm.docline)) {
        if (
line.height != 0updateLineHeight(line0);
        if (
line.widgets && cur && cur.previousSibling) for (var 0line.widgets.length; ++i) {
          var 
line.widgets[i];
          if (
w.showIfHidden) {
            var 
prev cur.previousSibling;
            if (/
pre/i.test(prev.nodeName)) {
              var 
wrap elt("div"nullnull"position: relative");
              
prev.parentNode.replaceChild(wrapprev);
              
wrap.appendChild(prev);
              
prev wrap;
            }
            var 
wnode prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
            if (!
w.handleMouseEventswnode.ignoreEvents true;
            
positionLineWidget(wwnodeprevdims);
          }
        }
      } else if (
nextIntact && nextIntact.from <= lineN && nextIntact.to lineN) {
        
// This line is intact. Skip to the actual node. Update its
        // line number if needed.
        
while (cur.lineObj != linecur rm(cur);
        if (
lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
          
setTextContent(cur.lineNumberlineNumberFor(cm.optionslineN));
        
cur cur.nextSibling;
      } else {
        
// For lines with widgets, make an attempt to find and reuse
        // the existing element, so that widgets aren't needlessly
        // removed and re-inserted into the dom
        
if (line.widgets) for (var 0search curreusesearch && 20; ++jsearch search.nextSibling)
          if (
search.lineObj == line && /div/i.test(search.nodeName)) { reuse search; break; }
        
// This line needs to be generated.
        
var lineNode buildLineElement(cmlinelineNdimsreuse);
        if (
lineNode != reuse) {
          
container.insertBefore(lineNodecur);
        } else {
          while (
cur != reusecur rm(cur);
          
cur cur.nextSibling;
        }

        
lineNode.lineObj line;
      }
      ++
lineN;
    });
    while (
curcur rm(cur);
  }

  function 
buildLineElement(cmlinelineNodimsreuse) {
    var 
lineElement lineContent(cmline);
    var 
markers line.gutterMarkersdisplay cm.displaywrap;

    if (!
cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
      return 
lineElement;

    
// Lines with gutter elements, widgets or a background class need
    // to be wrapped again, and have the extra elements added to the
    // wrapper div

    
if (reuse) {
      
reuse.alignable null;
      var 
isOk truewidgetsSeen 0insertBefore null;
      for (var 
reuse.firstChildnextnnext) {
        
next n.nextSibling;
        if (!/
bCodeMirror-linewidgetb/.test(n.className)) {
          
reuse.removeChild(n);
        } else {
          for (var 
0line.widgets.length; ++i) {
            var 
widget line.widgets[i];
            if (
widget.node == n.firstChild) {
              if (!
widget.above && !insertBeforeinsertBefore n;
              
positionLineWidget(widgetnreusedims);
              ++
widgetsSeen;
              break;
            }
          }
          if (
== line.widgets.length) { isOk false; break; }
        }
      }
      
reuse.insertBefore(lineElementinsertBefore);
      if (
isOk && widgetsSeen == line.widgets.length) {
        
wrap reuse;
        
reuse.className line.wrapClass || "";
      }
    }
    if (!
wrap) {
      
wrap elt("div"nullline.wrapClass"position: relative");
      
wrap.appendChild(lineElement);
    }
    
// Kludge to make sure the styled element lies behind the selection (by z-index)
    
if (line.bgClass)
      
wrap.insertBefore(elt("div"nullline.bgClass " CodeMirror-linebackground"), wrap.firstChild);
    if (
cm.options.lineNumbers || markers) {
      var 
gutterWrap wrap.insertBefore(elt("div"nullnull"position: absolute; left: " +
                                             (
cm.options.fixedGutter dims.fixedPos : -dims.gutterTotalWidth) + "px"),
                                         
wrap.firstChild);
      if (
cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
      if (
cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
        
wrap.lineNumber gutterWrap.appendChild(
          
elt("div"lineNumberFor(cm.optionslineNo),
              
"CodeMirror-linenumber CodeMirror-gutter-elt",
              
"left: " dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
              
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"));
        }
    }
    if (
ie_lt8wrap.style.zIndex 2;
    if (
line.widgets && wrap != reuse) for (var 0ws line.widgetsws.length; ++i) {
      var 
widget ws[i], node elt("div", [widget.node], "CodeMirror-linewidget");
      if (!
widget.handleMouseEventsnode.ignoreEvents true;
      
positionLineWidget(widgetnodewrapdims);
      if (
widget.above)
        
wrap.insertBefore(nodecm.options.lineNumbers && line.height != gutterWrap lineElement);
      else
        
wrap.appendChild(node);
      
signalLater(widget"redraw");
    }
    return 
wrap;
  }

  function 
positionLineWidget(widgetnodewrapdims) {
    if (
widget.noHScroll) {
      (
wrap.alignable || (wrap.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";
    }
  }

  
// SELECTION / CURSOR

  
function updateSelection(cm) {
    var 
display cm.display;
    var 
collapsed posEq(cm.doc.sel.fromcm.doc.sel.to);
    if (
collapsed || cm.options.showCursorWhenSelecting)
      
updateSelectionCursor(cm);
    else
      
display.cursor.style.display display.otherCursor.style.display "none";
    if (!
collapsed)
      
updateSelectionRange(cm);
    else
      
display.selectionDiv.style.display "none";

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

  
// No selection, plain cursor
  
function updateSelectionCursor(cm) {
    var 
display cm.displaypos cursorCoords(cmcm.doc.sel.head"div");
    
display.cursor.style.left pos.left "px";
    
display.cursor.style.top pos.top "px";
    
display.cursor.style.height Math.max(0pos.bottom pos.top) * cm.options.cursorHeight "px";
    
display.cursor.style.display "";

    if (
pos.other) {
      
display.otherCursor.style.display "";
      
display.otherCursor.style.left pos.other.left "px";
      
display.otherCursor.style.top pos.other.top "px";
      
display.otherCursor.style.height = (pos.other.bottom pos.other.top) * .85 "px";
    } else { 
display.otherCursor.style.display "none"; }
  }

  
// Highlight selection
  
function updateSelectionRange(cm) {
    var 
display cm.displaydoc cm.docsel cm.doc.sel;
    var 
fragment document.createDocumentFragment();
    var 
clientWidth display.lineSpace.offsetWidthpl paddingLeft(cm.display);

    function 
add(lefttopwidthbottom) {
      if (
top 0top 0;
      
fragment.appendChild(elt("div"null"CodeMirror-selected""position: absolute; left: " left +
                               
"px; top: " top "px; width: " + (width == null clientWidth 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 pl;
        if (
rightPos.top leftPos.top 3) { // Different lines, draw top part
          
add(leftleftPos.topnullleftPos.bottom);
          
left pl;
          if (
leftPos.bottom rightPos.topadd(leftleftPos.bottomnullrightPos.top);
        }
        if (
toArg == null && to == lineLenright clientWidth;
        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 pl 1left pl;
        
add(leftrightPos.topright leftrightPos.bottom);
      });
      return {
startstartendend};
    }

    if (
sel.from.line == sel.to.line) {
      
drawForLine(sel.from.linesel.from.chsel.to.ch);
    } else {
      var 
fromLine getLine(docsel.from.line), toLine getLine(docsel.to.line);
      var 
singleVLine visualLine(docfromLine) == visualLine(doctoLine);
      var 
leftEnd drawForLine(sel.from.linesel.from.chsingleVLine fromLine.text.length null).end;
      var 
rightStart drawForLine(sel.to.linesingleVLine nullsel.to.ch).start;
      if (
singleVLine) {
        if (
leftEnd.top rightStart.top 2) {
          
add(leftEnd.rightleftEnd.topnullleftEnd.bottom);
          
add(plrightStart.toprightStart.leftrightStart.bottom);
        } else {
          
add(leftEnd.rightleftEnd.toprightStart.left leftEnd.rightleftEnd.bottom);
        }
      }
      if (
leftEnd.bottom rightStart.top)
        
add(plleftEnd.bottomnullrightStart.top);
    }

    
removeChildrenAndAdd(display.selectionDivfragment);
    
display.selectionDiv.style.display "";
  }

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

  
// HIGHLIGHT WORKER

  
function startWorker(cmtime) {
    if (
cm.doc.mode.startState && cm.doc.frontier cm.display.showingTo)
      
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.showingTo) return;
    var 
end = +new Date cm.options.workTime;
    var 
state copyState(doc.modegetStateBefore(cmdoc.frontier));
    var 
changed = [], prevChange;
    
doc.iter(doc.frontierMath.min(doc.first doc.sizecm.display.showingTo 500), function(line) {
      if (
doc.frontier >= cm.display.showingFrom) { // Visible
        
var oldStyles line.styles;
        
line.styles highlightLine(cmlinestate);
        var 
ischange = !oldStyles || oldStyles.length != line.styles.length;
        for (var 
0; !ischange && oldStyles.length; ++iischange oldStyles[i] != line.styles[i];
        if (
ischange) {
          if (
prevChange && prevChange.end == doc.frontierprevChange.end++;
          else 
changed.push(prevChange = {startdoc.frontierenddoc.frontier 1});
        }
        
line.stateAfter copyState(doc.modestate);
      } else {
        
processLine(cmlinestate);
        
line.stateAfter doc.frontier == copyState(doc.modestate) : null;
      }
      ++
doc.frontier;
      if (+new 
Date end) {
        
startWorker(cmcm.options.workDelay);
        return 
true;
      }
    });
    if (
changed.length)
      
operation(cm, function() {
        for (var 
0changed.length; ++i)
          
regChange(thischanged[i].startchanged[i].end);
      })();
  }

  
// 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.docmaxScan cm.doc.mode.innerMode 1000 100;
    for (var 
search nlim maxScansearch 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(cmlinestate);
      var 
save pos == || pos == || pos >= display.showingFrom && pos display.showingTo;
      
line.stateAfter save copyState(doc.modestate) : null;
      ++
pos;
    });
    return 
state;
  }

  
// POSITION MEASUREMENT

  
function paddingTop(display) {return display.lineSpace.offsetTop;}
  function 
paddingVert(display) {return display.mover.offsetHeight display.lineSpace.offsetHeight;}
  function 
paddingLeft(display) {
    var 
removeChildrenAndAdd(display.measureelt("pre"nullnull"text-align: left")).appendChild(elt("span""x"));
    return 
e.offsetLeft;
  }

  function 
measureChar(cmlinechdatabias) {
    var 
dir = -1;
    
data data || measureLine(cmline);

    for (var 
pos ch;; pos += dir) {
      var 
data[pos];
      if (
r) break;
      if (
dir && pos == 0dir 1;
    }
    
bias pos ch "left" pos ch "right" bias;
    if (
bias == "left" && r.leftSider.leftSide;
    else if (
bias == "right" && r.rightSider.rightSide;
    return {
leftpos ch r.right r.left,
            
rightpos ch r.left r.right,
            
topr.top,
            
bottomr.bottom};
  }

  function 
findCachedMeasurement(cmline) {
    var 
cache cm.display.measureLineCache;
    for (var 
0cache.length; ++i) {
      var 
memo cache[i];
      if (
memo.text == line.text && memo.markedSpans == line.markedSpans &&
          
cm.display.scroller.clientWidth == memo.width &&
          
memo.classes == line.textClass "|" line.bgClass "|" line.wrapClass)
        return 
memo;
    }
  }

  function 
clearCachedMeasurement(cmline) {
    var 
exists findCachedMeasurement(cmline);
    if (
existsexists.text exists.measure exists.markedSpans null;
  }

  function 
measureLine(cmline) {
    
// First look in the cache
    
var cached findCachedMeasurement(cmline);
    if (
cached) return cached.measure;

    
// Failing that, recompute and store result in cache
    
var measure measureLineInner(cmline);
    var 
cache cm.display.measureLineCache;
    var 
memo = {textline.textwidthcm.display.scroller.clientWidth,
                
markedSpansline.markedSpansmeasuremeasure,
                
classesline.textClass "|" line.bgClass "|" line.wrapClass};
    if (
cache.length == 16cache[++cm.display.measureLineCachePos 16] = memo;
    else 
cache.push(memo);
    return 
measure;
  }

  function 
measureLineInner(cmline) {
    var 
display cm.displaymeasure emptyArray(line.text.length);
    var 
pre lineContent(cmlinemeasuretrue);

    
// IE does not cache element positions of inline elements between
    // calls to getBoundingClientRect. This makes the loop below,
    // which gathers the positions of all the characters on the line,
    // do an amount of layout work quadratic to the number of
    // characters. When line wrapping is off, we try to improve things
    // by first subdividing the line into a bunch of inline blocks, so
    // that IE can reuse most of the layout information from caches
    // for those blocks. This does interfere with line wrapping, so it
    // doesn't work when wrapping is on, but in that case the
    // situation is slightly better, since IE does cache line-wrapping
    // information and only recomputes per-line.
    
if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length 100) {
      var 
fragment document.createDocumentFragment();
      var 
chunk 10pre.childNodes.length;
      for (var 
0chunks Math.ceil(chunk); chunks; ++i) {
        var 
wrap elt("div"nullnull"display: inline-block");
        for (var 
0chunk && n; ++j) {
          
wrap.appendChild(pre.firstChild);
          --
n;
        }
        
fragment.appendChild(wrap);
      }
      
pre.appendChild(fragment);
    }

    
removeChildrenAndAdd(display.measurepre);

    var 
outer getRect(display.lineDiv);
    var 
vranges = [], data emptyArray(line.text.length), maxBot pre.offsetHeight;
    
// Work around an IE7/8 bug where it will sometimes have randomly
    // replaced our pre with a clone at this point.
    
if (ie_lt9 && display.measure.first != pre)
      
removeChildrenAndAdd(display.measurepre);

    function 
measureRect(rect) {
      var 
top rect.top outer.topbot rect.bottom outer.top;
      if (
bot maxBotbot maxBot;
      if (
top 0top 0;
      for (var 
vranges.length 2>= 0-= 2) {
        var 
rtop vranges[i], rbot vranges[i+1];
        if (
rtop bot || rbot top) continue;
        if (
rtop <= top && rbot >= bot ||
            
top <= rtop && bot >= rbot ||
            
Math.min(botrbot) - Math.max(toprtop) >= (bot top) >> 1) {
          
vranges[i] = Math.min(toprtop);
          
vranges[i+1] = Math.max(botrbot);
          break;
        }
      }
      if (
0) { vranges.lengthvranges.push(topbot); }
      return {
leftrect.left outer.left,
              
rightrect.right outer.left,
              
topibottomnull};
    }
    function 
finishRect(rect) {
      
rect.bottom vranges[rect.top+1];
      
rect.top vranges[rect.top];
    }

    for (var 
0curmeasure.length; ++i) if (cur measure[i]) {
      var 
node currect null;
      
// A widget might wrap, needs special care
      
if (/bCodeMirror-widgetb/.test(cur.className) && cur.getClientRects) {
        if (
cur.firstChild.nodeType == 1node cur.firstChild;
        var 
rects node.getClientRects();
        if (
rects.length 1) {
          
rect data[i] = measureRect(rects[0]);
          
rect.rightSide measureRect(rects[rects.length 1]);
        }
      }
      if (!
rectrect data[i] = measureRect(getRect(node));
      if (
cur.measureRightrect.right getRect(cur.measureRight).left;
      if (
cur.leftSiderect.leftSide measureRect(getRect(cur.leftSide));
    }
    
removeChildren(cm.display.measure);
    for (var 
0curdata.length; ++i) if (cur data[i]) {
      
finishRect(cur);
      if (
cur.leftSidefinishRect(cur.leftSide);
      if (
cur.rightSidefinishRect(cur.rightSide);
    }
    return 
data;
  }

  function 
measureLineWidth(cmline) {
    var 
hasBadSpan false;
    if (
line.markedSpans) for (var 0line.markedSpans; ++i) {
      var 
sp line.markedSpans[i];
      if (
sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan true;
    }
    var 
cached = !hasBadSpan && findCachedMeasurement(cmline);
    if (
cached) return measureChar(cmlineline.text.lengthcached.measure"right").right;

    var 
pre lineContent(cmlinenulltrue);
    var 
end pre.appendChild(zeroWidthElement(cm.display.measure));
    
removeChildrenAndAdd(cm.display.measurepre);
    return 
getRect(end).right getRect(cm.display.lineDiv).left;
  }

  function 
clearCaches(cm) {
    
cm.display.measureLineCache.length cm.display.measureLineCachePos 0;
    
cm.display.cachedCharWidth cm.display.cachedTextHeight 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; }

  
// Context is 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(cmlineObj);
    if (
context == "local"yOff += paddingTop(cm.display);
    else 
yOff -= cm.display.viewOffset;
    if (
context == "page" || context == "window") {
      var 
lOff getRect(cm.display.lineSpace);
      
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;
  }

  
// Context may be "window", "page", "div", or "local"/null
  // Result is in "div" coords
  
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 getRect(cm.display.sizer);
      
left += localBox.left;
      
top += localBox.top;
    }

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

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

  function 
cursorCoords(cmposcontextlineObjmeasurement) {
    
lineObj lineObj || getLine(cm.docpos.line);
    if (!
measurementmeasurement measureLine(cmlineObj);
    function 
get(chright) {
      var 
measureChar(cmlineObjchmeasurementright "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;
  }

  function 
PosWithInfo(linechoutsidexRel) {
    var 
pos = new Pos(linech);
    
pos.xRel xRel;
    if (
outsidepos.outside true;
    return 
pos;
  }

  
// Coords must be lineSpace-local
  
function coordsChar(cmxy) {
    var 
doc cm.doc;
    
+= cm.display.viewOffset;
    if (
0) return PosWithInfo(doc.first0true, -1);
    var 
lineNo lineAtHeight(docy), last doc.first doc.size 1;
    if (
lineNo last)
      return 
PosWithInfo(doc.first doc.size 1getLine(doclast).text.lengthtrue1);
    if (
00;

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

  function 
coordsCharInner(cmlineObjlineNoxy) {
    var 
innerOff heightAtLine(cmlineObj);
    var 
wrongLine falseadjust cm.display.wrapper.clientWidth;
    var 
measurement measureLine(cmlineObj);

    function 
getX(ch) {
      var 
sp cursorCoords(cmPos(lineNoch), "line",
                            
lineObjmeasurement);
      
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.test(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;
  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;
  }

  function 
charWidth(display) {
    if (
display.cachedCharWidth != null) return display.cachedCharWidth;
    var 
anchor elt("span""x");
    var 
pre elt("pre", [anchor]);
    
removeChildrenAndAdd(display.measurepre);
    var 
width anchor.offsetWidth;
    if (
width 2display.cachedCharWidth width;
    return 
width || 10;
  }

  
// OPERATIONS

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

  
var nextOpId 0;
  function 
startOperation(cm) {
    
cm.curOp = {
      
// An array of ranges of lines that have to be updated. See
      // updateDisplay.
      
changes: [],
      
forceUpdatefalse,
      
updateInputnull,
      
userSelChangenull,
      
textChangednull,
      
selectionChangedfalse,
      
cursorActivityfalse,
      
updateMaxLinefalse,
      
updateScrollPosfalse,
      
id: ++nextOpId
    
};
    if (!
delayedCallbackDepth++) delayedCallbacks = [];
  }

  function 
endOperation(cm) {
    var 
op cm.curOpdoc cm.docdisplay cm.display;
    
cm.curOp null;

    if (
op.updateMaxLinecomputeMaxLength(cm);
    if (
display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
      var 
width measureLineWidth(cmdisplay.maxLine);
      
display.sizer.style.minWidth Math.max(0width scrollerCutOff) + "px";
      
display.maxLineChanged false;
      var 
maxScrollLeft Math.max(0display.sizer.offsetLeft display.sizer.offsetWidth display.scroller.clientWidth);
      if (
maxScrollLeft doc.scrollLeft && !op.updateScrollPos)
        
setScrollLeft(cmMath.min(display.scroller.scrollLeftmaxScrollLeft), true);
    }
    var 
newScrollPosupdated;
    if (
op.updateScrollPos) {
      
newScrollPos op.updateScrollPos;
    } else if (
op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
      
var coords cursorCoords(cmdoc.sel.head);
      
newScrollPos calculateScrollPos(cmcoords.leftcoords.topcoords.leftcoords.bottom);
    }
    if (
op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) {
      
updated updateDisplay(cmop.changesnewScrollPos && newScrollPos.scrollTopop.forceUpdate);
      if (
cm.display.scroller.offsetHeightcm.doc.scrollTop cm.display.scroller.scrollTop;
    }
    if (!
updated && op.selectionChangedupdateSelection(cm);
    if (
op.updateScrollPos) {
      
display.scroller.scrollTop display.scrollbarV.scrollTop doc.scrollTop newScrollPos.scrollTop;
      
display.scroller.scrollLeft display.scrollbarH.scrollLeft doc.scrollLeft newScrollPos.scrollLeft;
      
alignHorizontally(cm);
      if (
op.scrollToPos)
        
scrollPosIntoView(cmclipPos(cm.docop.scrollToPos), op.scrollToPosMargin);
    } else if (
newScrollPos) {
      
scrollCursorIntoView(cm);
    }
    if (
op.selectionChangedrestartBlink(cm);

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

    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;
    }
    if (
op.textChanged)
      
signal(cm"change"cmop.textChanged);
    if (
op.cursorActivitysignal(cm"cursorActivity"cm);
    if (
delayed) for (var 0delayed.length; ++idelayed[i]();
  }

  
// Wraps a function in an operation. Returns the wrapped function.
  
function operation(cm1f) {
    return function() {
      var 
cm cm1 || thiswithOp = !cm.curOp;
      if (
withOpstartOperation(cm);
      try { var 
result f.apply(cmarguments); }
      finally { if (
withOpendOperation(cm); }
      return 
result;
    };
  }
  function 
docOperation(f) {
    return function() {
      var 
withOp this.cm && !this.cm.curOpresult;
      if (
withOpstartOperation(this.cm);
      try { 
result f.apply(thisarguments); }
      finally { if (
withOpendOperation(this.cm); }
      return 
result;
    };
  }
  function 
runInOp(cmf) {
    var 
withOp = !cm.curOpresult;
    if (
withOpstartOperation(cm);
    try { 
result f(); }
    finally { if (
withOpendOperation(cm); }
    return 
result;
  }

  function 
regChange(cmfromtolendiff) {
    if (
from == nullfrom cm.doc.first;
    if (
to == nullto cm.doc.first cm.doc.size;
    
cm.curOp.changes.push({fromfromtotodifflendiff});
  }

  
// INPUT HANDLING

  
function slowPoll(cm) {
    if (
cm.display.pollingFast) return;
    
cm.display.poll.set(cm.options.pollInterval, function() {
      
readInput(cm);
      if (
cm.state.focusedslowPoll(cm);
    });
  }

  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);
  }

  
// prevInput is a hack to work with IME. If we reset the textarea
  // on every change, that breaks IME. So we look for changes
  // compared to the previous content instead. (Modern browsers have
  // events that indicate IME taking place, but these are not widely
  // supported or compatible enough yet to rely on.)
  
function readInput(cm) {
    var 
input cm.display.inputprevInput cm.display.prevInputdoc cm.docsel doc.sel;
    if (!
cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
    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 (
text == prevInput && posEq(sel.fromsel.to)) return false;
    if (
ie && !ie_lt9 && cm.display.inputHasSelection === text) {
      
resetInput(cmtrue);
      return 
false;
    }

    var 
withOp = !cm.curOp;
    if (
withOpstartOperation(cm);
    
sel.shift false;
    var 
same 0Math.min(prevInput.lengthtext.length);
    while (
same && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
    var 
from sel.fromto sel.to;
    if (
same prevInput.length)
      
from Pos(from.linefrom.ch - (prevInput.length same));
    else if (
cm.state.overwrite && posEq(fromto) && !cm.state.pasteIncoming)
      
to Pos(to.lineMath.min(getLine(docto.line).text.lengthto.ch + (text.length same)));

    var 
updateInput cm.curOp.updateInput;
    var 
changeEvent = {fromfromtototextsplitLines(text.slice(same)),
                       
origincm.state.pasteIncoming "paste" "+input"};
    
makeChange(cm.docchangeEvent"end");
    
cm.curOp.updateInput updateInput;
    
signalLater(cm"inputRead"cmchangeEvent);

    if (
text.length 1000 || text.indexOf("n") > -1input.value cm.display.prevInput "";
    else 
cm.display.prevInput text;
    if (
withOpendOperation(cm);
    
cm.state.pasteIncoming false;
    return 
true;
  }

  function 
resetInput(cmuser) {
    var 
minimalselecteddoc cm.doc;
    if (!
posEq(doc.sel.fromdoc.sel.to)) {
      
cm.display.prevInput "";
      
minimal hasCopyEvent &&
        (
doc.sel.to.line doc.sel.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_lt9cm.display.inputHasSelection content;
    } else if (
user) {
      
cm.display.prevInput cm.display.input.value "";
      if (
ie && !ie_lt9cm.display.inputHasSelection null;
    }
    
cm.display.inaccurateSelection minimal;
  }

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

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

  
// EVENT HANDLERS

  
function registerEventHandlers(cm) {
    var 
cm.display;
    
on(d.scroller"mousedown"operation(cmonMouseDown));
    if (
ie)
      
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(getLine(cm.docpos.line).textpos);
        
extendSelection(cm.docword.fromword.to);
      }));
    else
      
on(d.scroller"dblclick", function(e) { signalDOMEvent(cme) || e_preventDefault(e); });
    
on(d.lineSpace"selectstart", function(e) {
      if (!
eventInWidget(de)) e_preventDefault(e);
    });
    
// Gecko browsers fire contextmenu *after* opening the menu, at
    // which point we can't mess with it anymore. Context menu is
    // handled in onMouseDown for Gecko.
    
if (!captureMiddleClickon(d.scroller"contextmenu", function(e) {onContextMenu(cme);});

    
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);
    });

    
on(d.scroller"mousewheel", function(e){onScrollWheel(cme);});
    
on(d.scroller"DOMMouseScroll", function(e){onScrollWheel(cme);});

    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; });

    var 
resizeTimer;
    function 
onResize() {
      if (
resizeTimer == nullresizeTimer setTimeout(function() {
        
resizeTimer null;
        
// Might be a text scaling operation, clear size caches.
        
d.cachedCharWidth d.cachedTextHeight knownScrollbarWidth null;
        
clearCaches(cm);
        
runInOp(cmbind(regChangecm));
      }, 
100);
    }
    
on(window"resize"onResize);
    
// Above handler holds on to the editor and its data structures.
    // Here we poll to unregister it when the editor is no longer in
    // the document, so that it can be garbage-collected.
    
function unregister() {
      for (var 
d.wrapper.parentNode&& != document.bodyp.parentNode) {}
      if (
psetTimeout(unregister5000);
      else 
off(window"resize"onResize);
    }
    
setTimeout(unregister5000);

    
on(d.input"keyup"operation(cm, function(e) {
      if (
signalDOMEvent(cme) || cm.options.onKeyEvent && cm.options.onKeyEvent(cmaddStop(e))) return;
      if (
e.keyCode == 16cm.doc.sel.shift false;
    }));
    
on(d.input"input"bind(fastPollcm));
    
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) || cm.options.onDragEvent && cm.options.onDragEvent(cmaddStop(e))) return;
      
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;
      
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 
prepareCopy() {
      if (
d.inaccurateSelection) {
        
d.prevInput "";
        
d.inaccurateSelection false;
        
d.input.value cm.getSelection();
        
selectInput(d.input);
      }
    }
    
on(d.input"cut"prepareCopy);
    
on(d.input"copy"prepareCopy);

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

  function 
eventInWidget(displaye) {
    for (var 
e_target(e); != display.wrappern.parentNode) {
      if (!
|| n.ignoreEvents || n.parentNode == display.sizer && != display.mover) return true;
    }
  }

  function 
posFromMouse(cmeliberal) {
    var 
display cm.display;
    if (!
liberal) {
      var 
target e_target(e);
      if (
target == display.scrollbarH || target == display.scrollbarH.firstChild ||
          
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
          
target == display.scrollbarFiller || target == display.gutterFiller) return null;
    }
    var 
xyspace getRect(display.lineSpace);
    
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
    
try { e.clientXe.clientY; } catch (e) { return null; }
    return 
coordsChar(cmspace.leftspace.top);
  }

  var 
lastClicklastDoubleClick;
  function 
onMouseDown(e) {
    if (
signalDOMEvent(thise)) return;
    var 
cm thisdisplay cm.displaydoc cm.docsel doc.sel;
    
sel.shift e.shiftKey;

    if (
eventInWidget(displaye)) {
      if (!
webkit) {
        
display.scroller.draggable false;
        
setTimeout(function(){display.scroller.draggable true;}, 100);
      }
      return;
    }
    if (
clickInGutter(cme)) return;
    var 
start posFromMouse(cme);

    switch (
e_button(e)) {
    case 
3:
      if (
captureMiddleClickonContextMenu.call(cmcme);
      return;
    case 
2:
      if (
webkitcm.state.lastMiddleDown = +new Date;
      if (
startextendSelection(cm.docstart);
      
setTimeout(bind(focusInputcm), 20);
      
e_preventDefault(e);
      return;
    }
    
// For button 1, if it was clicked inside the editor
    // (posFromMouse returning non-null), we have to adjust the
    // selection.
    
if (!start) {if (e_target(e) == display.scrollere_preventDefault(e); return;}

    if (!
cm.state.focusedonFocus(cm);

    var 
now = +new Datetype "single";
    if (
lastDoubleClick && lastDoubleClick.time now 400 && posEq(lastDoubleClick.posstart)) {
      
type "triple";
      
e_preventDefault(e);
      
setTimeout(bind(focusInputcm), 20);
      
selectLine(cmstart.line);
    } else if (
lastClick && lastClick.time now 400 && posEq(lastClick.posstart)) {
      
type "double";
      
lastDoubleClick = {timenowposstart};
      
e_preventDefault(e);
      var 
word findWordAt(getLine(docstart.line).textstart);
      
extendSelection(cm.docword.fromword.to);
    } else { 
lastClick = {timenowposstart}; }

    var 
last start;
    if (
cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.fromsel.to) &&
        !
posLess(startsel.from) && !posLess(sel.tostart) && type == "single") {
      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);
          
extendSelection(cm.docstart);
          
focusInput(cm);
        }
      });
      
// 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);
      return;
    }
    
e_preventDefault(e);
    if (
type == "single"extendSelection(cm.docclipPos(docstart));

    var 
startstart sel.fromstartend sel.tolastPos start;

    function 
doSelect(cur) {
      if (
posEq(lastPoscur)) return;
      
lastPos cur;

      if (
type == "single") {
        
extendSelection(cm.docclipPos(docstart), cur);
        return;
      }

      
startstart clipPos(docstartstart);
      
startend clipPos(docstartend);
      if (
type == "double") {
        var 
word findWordAt(getLine(doccur.line).textcur);
        if (
posLess(curstartstart)) extendSelection(cm.docword.fromstartend);
        else 
extendSelection(cm.docstartstartword.to);
      } else if (
type == "triple") {
        if (
posLess(curstartstart)) extendSelection(cm.docstartendclipPos(docPos(cur.line0)));
        else 
extendSelection(cm.docstartstartclipPos(docPos(cur.line 10)));
      }
    }

    var 
editorSize getRect(display.wrapper);
    
// 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(cmetrue);
      if (!
cur) return;
      if (!
posEq(curlast)) {
        if (!
cm.state.focusedonFocus(cm);
        
last cur;
        
doSelect(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);
    }

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

  function 
clickInGutter(cme) {
    var 
display cm.display;
    try { var 
mX e.clientXmY e.clientY; }
    catch(
e) { return false; }

    if (
mX >= Math.floor(getRect(display.gutters).right)) return false;
    
e_preventDefault(e);
    if (!
hasHandler(cm"gutterClick")) return true;

    var 
lineBox getRect(display.lineDiv);
    if (
mY lineBox.bottom) return true;
    
mY -= lineBox.top display.viewOffset;

    for (var 
0cm.options.gutters.length; ++i) {
      var 
display.gutters.childNodes[i];
      if (
&& getRect(g).right >= mX) {
        var 
line lineAtHeight(cm.docmY);
        var 
gutter cm.options.gutters[i];
        
signalLater(cm"gutterClick"cmlineguttere);
        break;
      }
    }
    return 
true;
  }

  
// 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) || (cm.options.onDragEvent && cm.options.onDragEvent(cmaddStop(e))))
      return;
    
e_preventDefault(e);
    if (
ielastDrop = +new Date;
    var 
pos posFromMouse(cmetrue), files e.dataTransfer.files;
    if (!
pos || isReadOnly(cm)) return;
    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 = function() {
          
text[i] = reader.result;
          if (++
read == n) {
            
pos clipPos(cm.docpos);
            
makeChange(cm.doc, {frompostopostextsplitLines(text.join("n")), origin"paste"}, "around");
          }
        };
        
reader.readAsText(file);
      };
      for (var 
0n; ++iloadFile(files[i], i);
    } else {
      
// Don't do a replace if the drop happened inside of the selected text.
      
if (cm.state.draggingText && !(posLess(poscm.doc.sel.from) || posLess(cm.doc.sel.topos))) {
        
cm.state.draggingText(e);
        
// Ensure the editor is re-focused
        
setTimeout(bind(focusInputcm), 20);
        return;
      }
      try {
        var 
text e.dataTransfer.getData("Text");
        if (
text) {
          var 
curFrom cm.doc.sel.fromcurTo cm.doc.sel.to;
          
setSelection(cm.docpospos);
          if (
cm.state.draggingTextreplaceRange(cm.doc""curFromcurTo"paste");
          
cm.replaceSelection(textnull"paste");
          
focusInput(cm);
          
onFocus(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;

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

    
// 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;");
      if (
opera) {
        
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 (
operaimg.parentNode.removeChild(img);
    }
  }

  function 
setScrollTop(cmval) {
    if (
Math.abs(cm.doc.scrollTop val) < 2) return;
    
cm.doc.scrollTop val;
    if (!
geckoupdateDisplay(cm, [], val);
    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);
  }
  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) {
      for (var 
cur e.targetcur != scrollcur cur.parentNode) {
        if (
cur.lineObj) {
          
cm.display.currentWheelTarget cur;
          break;
        }
      }
    }

    
// 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 && !opera && 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;
    }

    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;
      }
    }
  }

  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 
doc cm.docprevShift doc.sel.shiftdone false;
    try {
      if (
isReadOnly(cm)) cm.state.suppressEdits true;
      if (
dropShiftdoc.sel.shift false;
      
done bound(cm) != Pass;
    } finally {
      
doc.sel.shift prevShift;
      
cm.state.suppressEdits false;
    }
    return 
done;
  }

  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;
  function 
handleKeyBinding(cme) {
    
// Handle auto 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);
      if (
ie_lt9) { e.oldKeyCode e.keyCodee.keyCode 0; }
      
signalLater(cm"keyHandled"cmnamee);
    }
    return 
handled;
  }

  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;
    if (!
cm.state.focusedonFocus(cm);
    if (
signalDOMEvent(cme) || cm.options.onKeyEvent && cm.options.onKeyEvent(cmaddStop(e))) return;
    if (
ie && e.keyCode == 27e.returnValue false;
    var 
code e.keyCode;
    
// IE does strange things with escape.
    
cm.doc.sel.shift code == 16 || e.shiftKey;
    
// First give onKeyEvent option a chance to handle this.
    
var handled handleKeyBinding(cme);
    if (
opera) {
      
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("");
    }
  }

  function 
onKeyPress(e) {
    var 
cm this;
    if (
signalDOMEvent(cme) || cm.options.onKeyEvent && cm.options.onKeyEvent(cmaddStop(e))) return;
    var 
keyCode e.keyCodecharCode e.charCode;
    if (
opera && keyCode == lastStoppedKey) {lastStoppedKey nulle_preventDefault(e); return;}
    if (((
opera && (!e.which || e.which 10)) || khtml) && handleKeyBinding(cme)) return;
    var 
ch String.fromCharCode(charCode == null keyCode charCode);
    if (
this.options.electricChars && this.doc.mode.electricChars &&
        
this.options.smartIndent && !isReadOnly(this) &&
        
this.doc.mode.electricChars.indexOf(ch) > -1)
      
setTimeout(operation(cm, function() {indentLine(cmcm.doc.sel.to.line"smart");}), 75);
    if (
handleCharBinding(cmech)) return;
    if (
ie && !ie_lt9cm.display.inputHasSelection null;
    
fastPoll(cm);
  }

  function 
onFocus(cm) {
    if (
cm.options.readOnly == "nocursor") return;
    if (!
cm.state.focused) {
      
signal(cm"focus"cm);
      
cm.state.focused true;
      if (
cm.display.wrapper.className.search(/bCodeMirror-focusedb/) == -1)
        
cm.display.wrapper.className += " CodeMirror-focused";
      if (!
cm.curOp) {
        
resetInput(cmtrue);
        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;
      
cm.display.wrapper.className cm.display.wrapper.className.replace(" CodeMirror-focused""");
    }
    
clearInterval(cm.display.blinker);
    
setTimeout(function() {if (!cm.state.focusedcm.doc.sel.shift false;}, 150);
  }

  var 
detectingSelectAll;
  function 
onContextMenu(cme) {
    if (
signalDOMEvent(cme"contextmenu")) return;
    var 
display cm.displaysel cm.doc.sel;
    if (
eventInWidget(displaye)) return;

    var 
pos posFromMouse(cme), scrollPos display.scroller.scrollTop;
    if (!
pos || opera) return; // Opera is difficult.
    
if (posEq(sel.fromsel.to) || posLess(possel.from) || !posLess(possel.to))
      
operation(cmsetSelection)(cm.docpospos);

    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: white; outline: none;" +
      
"border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
    
focusInput(cm);
    
resetInput(cmtrue);
    
// Adds "Select all" to context menu in FF
    
if (posEq(sel.fromsel.to)) display.input.value display.prevInput " ";

    function 
prepareSelectAllHack() {
      if (
display.input.selectionStart != null) {
        var 
extval display.input.value " " + (posEq(sel.fromsel.to) ? "" display.input.value);
        
display.prevInput " ";
        
display.input.selectionStart 1display.input.selectionEnd extval.length;
      }
    }
    function 
rehide() {
      
display.inputDiv.style.position "relative";
      
display.input.style.cssText oldCSS;
      if (
ie_lt9display.scrollbarV.scrollTop display.scroller.scrollTop scrollPos;
      
slowPoll(cm);

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

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

  
// UPDATING

  
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));
  };

  
// Make sure a position will be valid after the given change.
  
function clipPostChange(docchangepos) {
    if (!
posLess(change.frompos)) return clipPos(docpos);
    var 
diff = (change.text.length 1) - (change.to.line change.from.line);
    if (
pos.line change.to.line diff) {
      var 
preLine pos.line difflastLine doc.first doc.size 1;
      if (
preLine lastLine) return Pos(lastLinegetLine(doclastLine).text.length);
      return 
clipToLen(posgetLine(docpreLine).text.length);
    }
    if (
pos.line == change.to.line diff)
      return 
clipToLen(poslst(change.text).length + (change.text.length == change.from.ch 0) +
                       
getLine(docchange.to.line).text.length change.to.ch);
    var 
inside pos.line change.from.line;
    return 
clipToLen(poschange.text[inside].length + (inside change.from.ch));
  }

  
// Hint can be null|"end"|"start"|"around"|{anchor,head}
  
function computeSelAfterChange(docchangehint) {
    if (
hint && typeof hint == "object"// Assumed to be {anchor, head} object
      
return {anchorclipPostChange(docchangehint.anchor),
              
headclipPostChange(docchangehint.head)};

    if (
hint == "start") return {anchorchange.fromheadchange.from};

    var 
end changeEnd(change);
    if (
hint == "around") return {anchorchange.fromheadend};
    if (
hint == "end") return {anchorendheadend};

    
// hint is null, leave the selection alone as much as possible
    
var adjustPos = function(pos) {
      if (
posLess(poschange.from)) return pos;
      if (!
posLess(change.topos)) return end;

      var 
line pos.line change.text.length - (change.to.line change.from.line) - 1ch pos.ch;
      if (
pos.line == change.to.linech += end.ch change.to.ch;
      return 
Pos(linech);
    };
    return {
anchoradjustPos(doc.sel.anchor), headadjustPos(doc.sel.head)};
  }

  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};
  }

  
// Replace the range from from to to by the strings in replacement.
  // change is a {from, to, text [, origin]} object
  
function makeChange(docchangeselUpdateignoreReadOnly) {
    if (
doc.cm) {
      if (!
doc.cm.curOp) return operation(doc.cmmakeChange)(docchangeselUpdateignoreReadOnly);
      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>= 1; --i)
        
makeChangeNoReadonly(doc, {fromsplit[i].fromtosplit[i].totext: [""]});
      if (
split.length)
        
makeChangeNoReadonly(doc, {fromsplit[0].fromtosplit[0].totextchange.text}, selUpdate);
    } else {
      
makeChangeNoReadonly(docchangeselUpdate);
    }
  }

  function 
makeChangeNoReadonly(docchangeselUpdate) {
    var 
selAfter computeSelAfterChange(docchangeselUpdate);
    
addToHistory(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));
    });
  }

  function 
makeChangeFromHistory(doctype) {
    if (
doc.cm && doc.cm.state.suppressEdits) return;

    var 
hist doc.history;
    var 
event = (type == "undo" hist.done hist.undone).pop();
    if (!
event) return;

    var 
anti = {changes: [], anchorBeforeevent.anchorAfterheadBeforeevent.headAfter,
                
anchorAfterevent.anchorBeforeheadAfterevent.headBefore,
                
generationhist.generation};
    (
type == "undo" hist.undone hist.done).push(anti);
    
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)) {
        (
type == "undo" hist.done hist.undone).length 0;
        return;
      }

      
anti.changes.push(historyChangeFromChange(docchange));

      var 
after computeSelAfterChange(docchangenull)
                    : {
anchorevent.anchorBeforeheadevent.headBefore};
      
makeChangeSingleDoc(docchangeaftermergeOldSpans(docchange));
      var 
rebased = [];

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

  function 
shiftDoc(docdistance) {
    function 
shiftPos(pos) {return Pos(pos.line distancepos.ch);}
    
doc.first += distance;
    if (
doc.cmregChange(doc.cmdoc.firstdoc.firstdistance);
    
doc.sel.head shiftPos(doc.sel.head); doc.sel.anchor shiftPos(doc.sel.anchor);
    
doc.sel.from shiftPos(doc.sel.from); doc.sel.to shiftPos(doc.sel.to);
  }

  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.cmchangespansselAfter);
    else 
updateDoc(docchangespansselAfter);
  }

  function 
makeChangeSingleDocInEditor(cmchangespansselAfter) {
    var 
doc cm.docdisplay cm.displayfrom change.fromto change.to;

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

    if (!
posLess(doc.sel.headchange.from) && !posLess(change.todoc.sel.head))
      
cm.curOp.cursorActivity true;

    
updateDoc(docchangespansselAfterestimateHeight(cm));

    if (!
cm.options.lineWrapping) {
      
doc.iter(checkWidthStartfrom.line change.text.length, function(line) {
        var 
len lineLength(docline);
        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
    
regChange(cmfrom.lineto.line 1lendiff);

    if (
hasHandler(cm"change")) {
      var 
changeObj = {fromfromtoto,
                       
textchange.text,
                       
removedchange.removed,
                       
originchange.origin};
      if (
cm.curOp.textChanged) {
        for (var 
cur cm.curOp.textChangedcur.nextcur cur.next) {}
        
cur.next changeObj;
      } else 
cm.curOp.textChanged changeObj;
    }
  }

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

  
// POSITION OBJECT

  
function Pos(linech) {
    if (!(
this instanceof Pos)) return new Pos(linech);
    
this.line linethis.ch ch;
  }
  
CodeMirror.Pos Pos;

  function 
posEq(ab) {return a.line == b.line && a.ch == b.ch;}
  function 
posLess(ab) {return a.line b.line || (a.line == b.line && a.ch b.ch);}
  function 
copyPos(x) {return Pos(x.linex.ch);}

  
// SELECTION

  
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;}

  
// If shift is held, this will move the selection anchor. Otherwise,
  // it'll set the whole selection.
  
function extendSelection(docposotherbias) {
    if (
doc.sel.shift || doc.sel.extend) {
      var 
anchor doc.sel.anchor;
      if (
other) {
        var 
posBefore posLess(posanchor);
        if (
posBefore != posLess(otheranchor)) {
          
anchor pos;
          
pos other;
        } else if (
posBefore != posLess(posother)) {
          
pos other;
        }
      }
      
setSelection(docanchorposbias);
    } else {
      
setSelection(docposother || posbias);
    }
    if (
doc.cmdoc.cm.curOp.userSelChange true;
  }

  function 
filterSelectionChange(docanchorhead) {
    var 
obj = {anchoranchorheadhead};
    
signal(doc"beforeSelectionChange"docobj);
    if (
doc.cmsignal(doc.cm"beforeSelectionChange"doc.cmobj);
    
obj.anchor clipPos(docobj.anchor); obj.head clipPos(docobj.head);
    return 
obj;
  }

  
// Update the selection. Last two args are only used by
  // updateDoc, since they have to be expressed in the line
  // numbers before the update.
  
function setSelection(docanchorheadbiascheckAtomic) {
    if (!
checkAtomic && hasHandler(doc"beforeSelectionChange") || doc.cm && hasHandler(doc.cm"beforeSelectionChange")) {
      var 
filtered filterSelectionChange(docanchorhead);
      
head filtered.head;
      
anchor filtered.anchor;
    }

    var 
sel doc.sel;
    
sel.goalColumn null;
    
// Skip over atomic spans.
    
if (checkAtomic || !posEq(anchorsel.anchor))
      
anchor skipAtomic(docanchorbiascheckAtomic != "push");
    if (
checkAtomic || !posEq(headsel.head))
      
head skipAtomic(docheadbiascheckAtomic != "push");

    if (
posEq(sel.anchoranchor) && posEq(sel.headhead)) return;

    
sel.anchor anchorsel.head head;
    var 
inv posLess(headanchor);
    
sel.from inv head anchor;
    
sel.to inv anchor head;

    if (
doc.cm)
      
doc.cm.curOp.updateInput doc.cm.curOp.selectionChanged =
        
doc.cm.curOp.cursorActivity true;

    
signalLater(doc"cursorActivity"doc);
  }

  function 
reCheckSelection(cm) {
    
setSelection(cm.doccm.doc.sel.fromcm.doc.sel.tonull"push");
  }

  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 "from" "to"];
            if (
posEq(newPoscurPos)) {
              
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;
    }
  }

  
// SCROLLING

  
function scrollCursorIntoView(cm) {
    var 
coords scrollPosIntoView(cmcm.doc.sel.headcm.options.cursorScrollMargin);
    if (!
cm.state.focused) return;
    var 
display cm.displaybox getRect(display.sizer), 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 
hidden display.cursor.style.display == "none";
      if (
hidden) {
        
display.cursor.style.display "";
        
display.cursor.style.left coords.left "px";
        
display.cursor.style.top = (coords.top display.viewOffset) + "px";
      }
      
display.cursor.scrollIntoView(doScroll);
      if (
hiddendisplay.cursor.style.display "none";
    }
  }

  function 
scrollPosIntoView(cmposmargin) {
    if (
margin == nullmargin 0;
    for (;;) {
      var 
changed falsecoords cursorCoords(cmpos);
      var 
scrollPos calculateScrollPos(cmcoords.leftcoords.top margincoords.leftcoords.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;
    }
  }

  function 
scrollIntoView(cmx1y1x2y2) {
    var 
scrollPos calculateScrollPos(cmx1y1x2y2);
    if (
scrollPos.scrollTop != nullsetScrollTop(cmscrollPos.scrollTop);
    if (
scrollPos.scrollLeft != nullsetScrollLeft(cmscrollPos.scrollLeft);
  }

  function 
calculateScrollPos(cmx1y1x2y2) {
    var 
display cm.displaysnapMargin textHeight(cm.display);
    if (
y1 0y1 0;
    var 
screen display.scroller.clientHeight scrollerCutOffscreentop display.scroller.scrollTopresult = {};
    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 
screenw display.scroller.clientWidth scrollerCutOffscreenleft display.scroller.scrollLeft;
    
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;
  }

  function 
updateScrollPos(cmlefttop) {
    
cm.curOp.updateScrollPos = {scrollLeftleft == null cm.doc.scrollLeft left,
                                
scrollToptop == null cm.doc.scrollTop top};
  }

  function 
addToScrollPos(cmlefttop) {
    var 
pos cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeftcm.doc.scrollLeftscrollTopcm.doc.scrollTop});
    var 
scroll cm.display.scroller;
    
pos.scrollTop Math.max(0Math.min(scroll.scrollHeight scroll.clientHeightpos.scrollTop top));
    
pos.scrollLeft Math.max(0Math.min(scroll.scrollWidth scroll.clientWidthpos.scrollLeft left));
  }

  
// API UTILITIES

  
function indentLine(cmnhowaggressive) {
    var 
doc cm.doc;
    if (
how == nullhow "add";
    if (
how == "smart") {
      if (!
cm.doc.mode.indenthow "prev";
      else var 
state getStateBefore(cmn);
    }

    var 
tabSize cm.options.tabSize;
    var 
line getLine(docn), curSpace countColumn(line.textnulltabSize);
    var 
curSpaceString line.text.match(/^s*/)[0], indentation;
    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");
    
line.stateAfter null;
  }

  function 
changeLine(cmhandleop) {
    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)) regChange(cmnono 1);
    else return 
null;
    return 
line;
  }

  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";
      for (var 
first true;; first false) {
        if (
dir && !moveOnce(!first)) break;
        var 
cur lineObj.text.charAt(ch) || "n";
        var 
type isWordChar(cur) ? "w"
          
: !group null
          
: /s/.test(cur) ? null
          
"p";
        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;
  }

  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;
  }

  function 
findWordAt(linepos) {
    var 
start pos.chend pos.ch;
    if (
line) {
      if ((
pos.xRel || end == line.length) && start) --start; else ++end;
      var 
startChar line.charAt(start);
      var 
check isWordChar(startChar) ? isWordChar
        
: /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 {
fromPos(pos.linestart), toPos(pos.lineend)};
  }

  function 
selectLine(cmline) {
    
extendSelection(cm.docPos(line0), clipPos(cm.docPos(line 10)));
  }

  
// PROTOTYPE

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

  
CodeMirror.prototype = {
    
constructorCodeMirror,
    
focus: function(){window.focus(); focusInput(this); onFocus(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;
        }
    },

    
addOverlayoperation(null, 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);
    }),
    
removeOverlayoperation(null, 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;
        }
      }
    }),

    
indentLineoperation(null, 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);
    }),
    
indentSelectionoperation(null, function(how) {
      var 
sel this.doc.sel;
      if (
posEq(sel.fromsel.to)) return indentLine(thissel.from.linehow);
      var 
sel.to.line - (sel.to.ch 1);
      for (var 
sel.from.line<= e; ++iindentLine(thisihow);
    }),

    
// 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 mode.token(streamstate);
      }
      return {
startstream.start,
              
endstream.pos,
              
stringstream.current(),
              
classNamestyle || null// Deprecated, use 'type' instead
              
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;
      if (
ch == 0) return styles[2];
      for (;;) {
        var 
mid = (before after) >> 1;
        if ((
mid styles[mid 1] : 0) >= chafter mid;
        else if (
styles[mid 1] < chbefore mid 1;
        else return 
styles[mid 2];
      }
    },

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

    
getHelper: function(postype) {
      if (!
helpers.hasOwnProperty(type)) return;
      var 
help helpers[type], mode this.getModeAt(pos);
      return 
mode[type] && help[mode[type]] ||
        
mode.helperType && help[mode.helperType] ||
        
help[mode.name];
    },

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

    
cursorCoords: function(startmode) {
      var 
possel this.doc.sel;
      if (
start == nullpos sel.head;
      else if (
typeof start == "object"pos clipPos(this.docstart);
      else 
pos start sel.from sel.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(thisgetLine(this.docline), {top0left0}, mode || "page").top +
        (
end lineObj.height 0);
    },

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

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

    
clearGutteroperation(null, function(gutterID) {
      var 
cm thisdoc cm.docdoc.first;
      
doc.iter(function(line) {
        if (
line.gutterMarkers && line.gutterMarkers[gutterID]) {
          
line.gutterMarkers[gutterID] = null;
          
regChange(cmi1);
          if (
isEmpty(line.gutterMarkers)) line.gutterMarkers null;
        }
        ++
i;
      });
    }),

    
addLineClassoperation(null, function(handlewherecls) {
      return 
changeLine(thishandle, 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;
      });
    }),

    
removeLineClassoperation(null, function(handlewherecls) {
      return 
changeLine(thishandle, 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;
      });
    }),

    
addLineWidgetoperation(null, 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.showingFromtothis.display.showingTo};},

    
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);
    },

    
triggerOnKeyDownoperation(nullonKeyDown),

    
execCommand: function(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;
    },

    
moveHoperation(null, function(dirunit) {
      var 
sel this.doc.selpos;
      if (
sel.shift || sel.extend || posEq(sel.fromsel.to))
        
pos findPosH(this.docsel.headdirunitthis.options.rtlMoveVisually);
      else
        
pos dir sel.from sel.to;
      
extendSelection(this.docposposdir);
    }),

    
deleteHoperation(null, function(dirunit) {
      var 
sel this.doc.sel;
      if (!
posEq(sel.fromsel.to)) replaceRange(this.doc""sel.fromsel.to"+delete");
      else 
replaceRange(this.doc""sel.fromfindPosH(this.docsel.headdirunitfalse), "+delete");
      
this.curOp.userSelChange true;
    }),

    
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;
    },

    
moveVoperation(null, function(dirunit) {
      var 
sel this.doc.sel;
      var 
pos cursorCoords(thissel.head"div");
      if (
sel.goalColumn != nullpos.left sel.goalColumn;
      var 
target findPosV(thisposdirunit);

      if (
unit == "page"addToScrollPos(this0charCoords(thistarget"div").top pos.top);
      
extendSelection(this.doctargettargetdir);
      
sel.goalColumn pos.left;
    }),

    
toggleOverwrite: function(value) {
      if (
value != null && value == this.state.overwrite) return;
      if (
this.state.overwrite = !this.state.overwrite)
        
this.display.cursor.className += " CodeMirror-overwrite";
      else
        
this.display.cursor.className this.display.cursor.className.replace(" CodeMirror-overwrite""");
    },
    
hasFocus: function() { return this.state.focused; },

    
scrollTooperation(null, function(xy) {
      
updateScrollPos(thisxy);
    }),
    
getScrollInfo: function() {
      var 
scroller this.display.scrollerco scrollerCutOff;
      return {
leftscroller.scrollLefttopscroller.scrollTop,
              
heightscroller.scrollHeight cowidthscroller.scrollWidth co,
              
clientHeightscroller.clientHeight coclientWidthscroller.clientWidth co};
    },

    
scrollIntoViewoperation(null, function(posmargin) {
      if (
typeof pos == "number"pos Pos(pos0);
      if (!
marginmargin 0;
      var 
coords pos;

      if (!
pos || pos.line != null) {
        
this.curOp.scrollToPos pos clipPos(this.docpos) : this.doc.sel.head;
        
this.curOp.scrollToPosMargin margin;
        
coords cursorCoords(thisthis.curOp.scrollToPos);
      }
      var 
sPos calculateScrollPos(thiscoords.leftcoords.top margincoords.rightcoords.bottom margin);
      
updateScrollPos(thissPos.scrollLeftsPos.scrollTop);
    }),

    
setSizeoperation(null, 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.lineWrapping)
        
this.display.measureLineCache.length this.display.measureLineCachePos 0;
      
this.curOp.forceUpdate true;
    }),

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

    
refreshoperation(null, function() {
      
clearCaches(this);
      
updateScrollPos(thisthis.doc.scrollLeftthis.doc.scrollTop);
      
regChange(this);
    }),

    
swapDocoperation(null, function(doc) {
      var 
old this.doc;
      
old.cm null;
      
attachDoc(thisdoc);
      
clearCaches(this);
      
resetInput(thistrue);
      
updateScrollPos(thisdoc.scrollLeftdoc.scrollTop);
      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

  
var optionHandlers CodeMirror.optionHandlers = {};

  
// The default configuration options.
  
var defaults CodeMirror.defaults = {};

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

  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) {
    
loadMode(cm);
    
clearCaches(cm);
    
regChange(cm);
  }, 
true);
  
option("electricChars"true);
  
option("rtlMoveVisually", !windows);

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

  
option("onKeyEvent"null);
  
option("onDragEvent"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("readOnly"false, function(cmval) {
    if (
val == "nocursor") {onBlur(cm); cm.display.input.blur();}
    else if (!
valresetInput(cmtrue);
  });
  
option("dragDrop"true);

  
option("cursorBlinkRate"530);
  
option("cursorScrollMargin"0);
  
option("cursorHeight"1);
  
option("workTime"100);
  
option("workDelay"100);
  
option("flattenSpans"true);
  
option("pollInterval"100);
  
option("undoDepth"40, function(cmval){cm.doc.history.undoDepth val;});
  
option("historyEventDelay"500);
  
option("viewportMargin"10, function(cm){cm.refresh();}, true);
  
option("maxHighlightLength"10000, function(cm){loadMode(cm); cm.refresh();}, true);
  
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 = {};

  
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;
  };

  
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];
      
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"};
  };

  
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;

    return 
modeObj;
  };

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

  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] = {};
    
helpers[type][name] = value;
  };

  
// UTILITIES

  
CodeMirror.isWordChar isWordChar;

  
// MODE STATE HANDLING

  // Utility functions for working with state. Exported because modes
  // sometimes need to do this.
  
function copyState(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;
  }
  
CodeMirror.copyState copyState;

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

  
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

  
var commands CodeMirror.commands = {
    
selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
    
killLine: function(cm) {
      var 
from cm.getCursor(true), to cm.getCursor(false), sel = !posEq(fromto);
      if (!
sel && cm.getLine(from.line).length == from.ch)
        
cm.replaceRange(""fromPos(from.line 10), "+delete");
      else 
cm.replaceRange(""fromsel to Pos(from.line), "+delete");
    },
    
deleteLine: function(cm) {
      var 
cm.getCursor().line;
      
cm.replaceRange(""Pos(l0), Pos(l), "+delete");
    },
    
delLineLeft: function(cm) {
      var 
cur cm.getCursor();
      
cm.replaceRange(""Pos(cur.line0), cur"+delete");
    },
    
undo: function(cm) {cm.undo();},
    
redo: function(cm) {cm.redo();},
    
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
    
goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
    
goLineStart: function(cm) {
      
cm.extendSelection(lineStart(cmcm.getCursor().line));
    },
    
goLineStartSmart: function(cm) {
      var 
cur cm.getCursor(), start lineStart(cmcur.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 cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
        
cm.extendSelection(Pos(start.lineinWS firstNonWS));
      } else 
cm.extendSelection(start);
    },
    
goLineEnd: function(cm) {
      
cm.extendSelection(lineEnd(cmcm.getCursor().line));
    },
    
goLineRight: function(cm) {
      var 
top cm.charCoords(cm.getCursor(), "div").top 5;
      
cm.extendSelection(cm.coordsChar({leftcm.display.lineDiv.offsetWidth 100toptop}, "div"));
    },
    
goLineLeft: function(cm) {
      var 
top cm.charCoords(cm.getCursor(), "div").top 5;
      
cm.extendSelection(cm.coordsChar({left0toptop}, "div"));
    },
    
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""end""+input");},
    
defaultTab: function(cm) {
      if (
cm.somethingSelected()) cm.indentSelection("add");
      else 
cm.replaceSelection("t""end""+input");
    },
    
transposeChars: function(cm) {
      var 
cur cm.getCursor(), line cm.getLine(cur.line);
      if (
cur.ch && cur.ch line.length 1)
        
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch 1),
                        
Pos(cur.linecur.ch 1), Pos(cur.linecur.ch 1));
    },
    
newlineAndIndent: function(cm) {
      
operation(cm, function() {
        
cm.replaceSelection("n""end""+input");
        
cm.indentLine(cm.getCursor().linenulltrue);
      })();
    },
    
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""Tab""defaultTab""Shift-Tab""indentAuto",
    
"Enter""newlineAndIndent""Insert""toggleOverwrite"
  
};
  
// Note that the save and find-related commands aren't defined by
  // default. 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""Alt-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",
    
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",
    
fallthrough: ["basic""emacsy"]
  };
  
keyMap["default"] = mac keyMap.macDefault keyMap.pcDefault;
  
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 DISPATCH

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

  function 
lookupKey(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.lengthe; ++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";
    }
  }
  function 
isModifierKey(event) {
    var 
name keyNames[event.keyCode];
    return 
name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
  }
  function 
keyName(eventnoShift) {
    if (
opera && 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;
  }
  
CodeMirror.lookupKey lookupKey;
  
CodeMirror.isModifierKey isModifierKey;
  
CodeMirror.keyName keyName;

  
// 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 document.body;
      
// doc.activeElement occasionally throws on IE
      
try { hasFocus document.activeElement; } catch(e) {}
      
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.

  // The character stream used by a mode's parser.
  
function StringStream(stringtabSize) {
    
this.pos this.start 0;
    
this.string string;
    
this.tabSize tabSize || 8;
    
this.lastColumnPos this.lastColumnValue 0;
  }

  
StringStream.prototype = {
    
eol: function() {return this.pos >= this.string.length;},
    
sol: function() {return this.pos == 0;},
    
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;
    },
    
indentation: function() {return countColumn(this.stringnullthis.tabSize);},
    
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);}
  };
  
CodeMirror.StringStream StringStream;

  
// TEXTMARKERS

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

  
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 (
span.to != nullmax lineNo(line);
      
line.markedSpans removeMarkedSpan(line.markedSpansspan);
      if (
span.from != null)
        
min lineNo(line);
      else if (
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(cm.docthis.lines[i]), len lineLength(cm.docvisual);
      if (
len cm.display.maxLineLength) {
        
cm.display.maxLine visual;
        
cm.display.maxLineLength len;
        
cm.display.maxLineChanged true;
      }
    }

    if (
min != null && cmregChange(cmminmax 1);
    
this.lines.length 0;
    
this.explicitlyCleared true;
    if (
this.atomic && this.doc.cantEdit) {
      
this.doc.cantEdit false;
      if (
cmreCheckSelection(cm);
    }
    if (
withOpendOperation(cm);
  };

  
TextMarker.prototype.find = function() {
    var 
fromto;
    for (var 
0this.lines.length; ++i) {
      var 
line this.lines[i];
      var 
span getMarkedSpanFor(line.markedSpansthis);
      if (
span.from != null || span.to != null) {
        var 
found lineNo(line);
        if (
span.from != nullfrom Pos(foundspan.from);
        if (
span.to != nullto Pos(foundspan.to);
      }
    }
    if (
this.type == "bookmark") return from;
    return 
from && {fromfromtoto};
  };

  
TextMarker.prototype.changed = function() {
    var 
pos this.find(), cm this.doc.cm;
    if (!
pos || !cm) return;
    if (
this.type != "bookmark"pos pos.from;
    var 
line getLine(this.docpos.line);
    
clearCachedMeasurement(cmline);
    if (
pos.line >= cm.display.showingFrom && pos.line cm.display.showingTo) {
      for (var 
node cm.display.lineDiv.firstChildnodenode node.nextSibling) if (node.lineObj == line) {
        if (
node.offsetHeight != line.heightupdateLineHeight(linenode.offsetHeight);
        break;
      }
      
runInOp(cm, function() {
        
cm.curOp.selectionChanged cm.curOp.forceUpdate cm.curOp.updateMaxLine true;
      });
    }
  };

  
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);
    }
  };

  function 
markText(docfromtooptionstype) {
    if (
options && options.shared) return markTextShared(docfromtooptionstype);
    if (
doc.cm && !doc.cm.curOp) return operation(doc.cmmarkText)(docfromtooptionstype);

    var 
marker = new TextMarker(doctype);
    if (
type == "range" && !posLess(fromto)) return marker;
    if (
optionscopyObj(optionsmarker);
    if (
marker.replacedWith) {
      
marker.collapsed true;
      
marker.replacedWith elt("span", [marker.replacedWith], "CodeMirror-widget");
      if (!
options.handleMouseEventsmarker.replacedWith.ignoreEvents true;
    }
    if (
marker.collapsedsawCollapsedSpans true;

    if (
marker.addToHistory)
      
addToHistory(doc, {fromfromtotoorigin"markText"},
                   {
headdoc.sel.headanchordoc.sel.anchor}, NaN);

    var 
curLine from.linesize 0collapsedAtStartcollapsedAtEndcm doc.cmupdateMaxLine;
    
doc.iter(curLineto.line 1, function(line) {
      if (
cm && marker.collapsed && !cm.options.lineWrapping && visualLine(docline) == cm.display.maxLine)
        
updateMaxLine true;
      var 
span = {fromnulltonullmarkermarker};
      
size += line.text.length;
      if (
curLine == from.line) {span.from from.chsize -= from.ch;}
      if (
curLine == to.line) {span.to to.chsize -= line.text.length to.ch;}
      if (
marker.collapsed) {
        if (
curLine == to.linecollapsedAtEnd collapsedSpanAt(lineto.ch);
        if (
curLine == from.linecollapsedAtStart collapsedSpanAt(linefrom.ch);
        else 
updateLineHeight(line0);
      }
      
addMarkedSpan(linespan);
      ++
curLine;
    });
    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) {
      if (
collapsedAtStart != collapsedAtEnd)
        throw new 
Error("Inserting collapsed marker overlapping an existing one");
      
marker.size size;
      
marker.atomic true;
    }
    if (
cm) {
      if (
updateMaxLinecm.curOp.updateMaxLine true;
      if (
marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
        
regChange(cmfrom.lineto.line 1);
      if (
marker.atomicreCheckSelection(cm);
    }
    return 
marker;
  }

  
// SHARED TEXTMARKERS

  
function SharedTextMarker(markersprimary) {
    
this.markers markers;
    
this.primary primary;
    for (var 
0me thismarkers.length; ++i) {
      
markers[i].parent this;
      
on(markers[i], "clear", function(){me.clear();});
    }
  }
  
CodeMirror.SharedTextMarker SharedTextMarker;
  
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() {
    return 
this.primary.find();
  };

  function 
markTextShared(docfromtooptionstype) {
    
options copyObj(options);
    
options.shared false;
    var 
markers = [markText(docfromtooptionstype)], primary markers[0];
    var 
widget options.replacedWith;
    
linkedDocs(doc, function(doc) {
      if (
widgetoptions.replacedWith 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);
  }

  
// TEXTMARKER SPANS

  
function getMarkedSpanFor(spansmarker) {
    if (
spans) for (var 0spans.length; ++i) {
      var 
span spans[i];
      if (
span.marker == marker) return span;
    }
  }
  function 
removeMarkedSpan(spansspan) {
    for (var 
r0spans.length; ++i)
      if (
spans[i] != span) (|| (= [])).push(spans[i]);
    return 
r;
  }
  function 
addMarkedSpan(linespan) {
    
line.markedSpans line.markedSpans line.markedSpans.concat([span]) : [span];
    
span.marker.attachLine(line);
  }

  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 || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
        var 
endsAfter span.to == null || (marker.inclusiveRight span.to >= startCh span.to startCh);
        (
nw || (nw = [])).push({fromspan.from,
                                
toendsAfter null span.to,
                                
markermarker});
      }
    }
    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 || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
        var 
startsBefore span.from == null || (marker.inclusiveLeft span.from <= endCh span.from endCh);
        (
nw || (nw = [])).push({fromstartsBefore null span.from endCh,
                                
tospan.to == null null span.to endCh,
                                
markermarker});
      }
    }
    return 
nw;
  }

  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 posEq(change.fromchange.to);
    
// 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);
        }
      }
    }
    if (
sameLine && first) {
      
// Make sure we didn't create any zero-length spans
      
for (var 0first.length; ++i)
        if (
first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
          
first.splice(i--, 1);
      if (!
first.lengthfirst null;
    }

    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({fromnulltonullmarkerfirst[i].marker});
      for (var 
0gap; ++i)
        
newMarkers.push(gapMarkers);
      
newMarkers.push(last);
    }
    return 
newMarkers;
  }

  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;
  }

  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();
      for (var 
0parts.length; ++j) {
        var 
parts[j];
        if (
posLess(p.tom.from) || posLess(m.top.from)) continue;
        var 
newParts = [j1];
        if (
posLess(p.fromm.from) || !mk.inclusiveLeft && posEq(p.fromm.from))
          
newParts.push({fromp.fromtom.from});
        if (
posLess(m.top.to) || !mk.inclusiveRight && posEq(p.tom.to))
          
newParts.push({fromm.totop.to});
        
parts.splice.apply(partsnewParts);
        
+= newParts.length 1;
      }
    }
    return 
parts;
  }

  function 
collapsedSpanAt(linech) {
    var 
sps sawCollapsedSpans && line.markedSpansfound;
    if (
sps) for (var sp0sps.length; ++i) {
      
sp sps[i];
      if (!
sp.marker.collapsed) continue;
      if ((
sp.from == null || sp.from ch) &&
          (
sp.to == null || sp.to ch) &&
          (!
found || found.width sp.marker.width))
        
found sp.marker;
    }
    return 
found;
  }
  function 
collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
  function 
collapsedSpanAtEnd(line) { return collapsedSpanAt(lineline.text.length 1); }

  function 
visualLine(docline) {
    var 
merged;
    while (
merged collapsedSpanAtStart(line))
      
line getLine(docmerged.find().from.line);
    return 
line;
  }

  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.replacedWith) continue;
      if (
sp.from == && sp.marker.inclusiveLeft && lineIsHiddenInner(doclinesp))
        return 
true;
    }
  }
  function 
lineIsHiddenInner(doclinespan) {
    if (
span.to == null) {
      var 
end span.marker.find().toendLine getLine(docend.line);
      return 
lineIsHiddenInner(docendLinegetMarkedSpanFor(endLine.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.replacedWith && sp.from == span.to &&
          (
sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
          
lineIsHiddenInner(doclinesp)) return true;
    }
  }

  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;
  }

  
// LINE WIDGETS

  
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 
widgetOperation(f) {
    return function() {
      var 
withOp = !this.cm.curOp;
      if (
withOpstartOperation(this.cm);
      try {var 
result f.apply(thisarguments);}
      finally {if (
withOpendOperation(this.cm);}
      return 
result;
    };
  }
  
LineWidget.prototype.clear widgetOperation(function() {
    var 
ws this.line.widgetsno lineNo(this.line);
    if (
no == null || !ws) return;
    for (var 
0ws.length; ++i) if (ws[i] == thisws.splice(i--, 1);
    if (!
ws.lengththis.line.widgets null;
    var 
aboveVisible heightAtLine(this.cmthis.line) < this.cm.doc.scrollTop;
    
updateLineHeight(this.lineMath.max(0this.line.height widgetHeight(this)));
    if (
aboveVisibleaddToScrollPos(this.cm0, -this.height);
    
regChange(this.cmnono 1);
  });
  
LineWidget.prototype.changed widgetOperation(function() {
    var 
oldH this.height;
    
this.height null;
    var 
diff widgetHeight(this) - oldH;
    if (!
diff) return;
    
updateLineHeight(this.linethis.line.height diff);
    var 
no lineNo(this.line);
    
regChange(this.cmnono 1);
  });

  function 
widgetHeight(widget) {
    if (
widget.height != null) return widget.height;
    if (!
widget.node.parentNode || widget.node.parentNode.nodeType != 1)
      
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, 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) || widget.showIfHidden) {
        var 
aboveVisible heightAtLine(cmline) < cm.doc.scrollTop;
        
updateLineHeight(lineline.height widgetHeight(widget));
        if (
aboveVisibleaddToScrollPos(cm0widget.height);
      }
      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);

  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);
  }

  function 
cleanUpLine(line) {
    
line.parent null;
    
detachMarkedSpans(line);
  }

  
// Run the given mode's parser over a line, update the styles
  // array, which contains alternating fragments of text and CSS
  // classes.
  
function runMode(cmtextmodestatef) {
    var 
flattenSpans mode.flattenSpans;
    if (
flattenSpans == nullflattenSpans cm.options.flattenSpans;
    var 
curStart 0curStyle null;
    var 
stream = new StringStream(textcm.options.tabSize), style;
    if (
text == "" && mode.blankLinemode.blankLine(state);
    while (!
stream.eol()) {
      if (
stream.pos cm.options.maxHighlightLength) {
        
flattenSpans false;
        
// Webkit seems to refuse to render text nodes longer than 57444 characters
        
stream.pos Math.min(text.lengthstream.start 50000);
        
style null;
      } else {
        
style mode.token(streamstate);
      }
      if (!
flattenSpans || curStyle != style) {
        if (
curStart stream.startf(stream.startcurStyle);
        
curStart stream.startcurStyle style;
      }
      
stream.start stream.pos;
    }
    if (
curStart stream.posf(stream.poscurStyle);
  }

  function 
highlightLine(cmlinestate) {
    
// 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];
    
// Compute the base array of styles
    
runMode(cmline.textcm.doc.modestate, function(endstyle) {st.push(endstyle);});

    
// 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(startstartendstyle);
          
start 2;
        } else {
          for (; 
start istart += 2) {
            var 
cur st[start+1];
            
st[start+1] = cur cur " " style style;
          }
        }
      });
    }

    return 
st;
  }

  function 
getLineStyles(cmline) {
    if (!
line.styles || line.styles[0] != cm.state.modeGen)
      
line.styles highlightLine(cmlineline.stateAfter getStateBefore(cmlineNo(line)));
    return 
line.styles;
  }

  
// Lightweight form of highlight -- proceed over this line and
  // update state, but don't save a style array.
  
function processLine(cmlinestate) {
    var 
mode cm.doc.mode;
    var 
stream = new StringStream(line.textcm.options.tabSize);
    if (
line.text == "" && mode.blankLinemode.blankLine(state);
    while (!
stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
      
mode.token(streamstate);
      
stream.start stream.pos;
    }
  }

  var 
styleToClassCache = {};
  function 
styleToClass(style) {
    if (!
style) return null;
    return 
styleToClassCache[style] ||
      (
styleToClassCache[style] = "cm-" style.replace(/ +/g" cm-"));
  }

  function 
lineContent(cmrealLinemeasurecopyWidgets) {
    var 
mergedline realLine, empty = true;
    while (
merged collapsedSpanAtStart(line))
      
line getLine(cm.docmerged.find().from.line);

    var 
builder = {preelt("pre"), col0pos0,
                   
measurenullmeasuredSomethingfalsecmcm,
                   
copyWidgetscopyWidgets};
    if (
line.textClassbuilder.pre.className line.textClass;

    do {
      if (
line.text) empty = false;
      
builder.measure line == realLine && measure;
      
builder.pos 0;
      
builder.addToken builder.measure buildTokenMeasure buildToken;
      if ((
ie || webkit) && cm.getOption("lineWrapping"))
        
builder.addToken buildTokenSplitSpaces(builder.addToken);
      var 
next insertLineContent(linebuildergetLineStyles(cmline));
      if (
measure && line == realLine && !builder.measuredSomething) {
        
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
        
builder.measuredSomething true;
      }
      if (
nextline getLine(cm.docnext.to.line);
    } while (
next);

    if (
measure && !builder.measuredSomething && !measure[0])
      
measure[0] = builder.pre.appendChild(empty ? elt("span""u00a0") : zeroWidthElement(cm.display.measure));
    if (!
builder.pre.firstChild && !lineIsHidden(cm.docrealLine))
      
builder.pre.appendChild(document.createTextNode("u00a0"));

    var 
order;
    
// Work around problem with the reported dimensions of single-char
    // direction spans on IE (issue #1129). See also the comment in
    // cursorCoords.
    
if (measure && ie && (order getOrder(line))) {
      var 
order.length 1;
      if (
order[l].from == order[l].to) --l;
      var 
last order[l], prev order[1];
      if (
last.from == last.to && prev && last.level prev.level) {
        var 
span measure[builder.pos 1];
        if (
spanspan.parentNode.insertBefore(span.measureRight zeroWidthElement(cm.display.measure),
                                               
span.nextSibling);
      }
    }

    
signal(cm"renderLine"cmrealLinebuilder.pre);
    return 
builder.pre;
  }

  var 
tokenSpecialChars = /[tu0000-u0019u00adu200bu2028u2029uFEFF]/g;
  function 
buildToken(buildertextstylestartStyleendStyletitle) {
    if (!
text) return;
    if (!
tokenSpecialChars.test(text)) {
      
builder.col += text.length;
      var 
content document.createTextNode(text);
    } else {
      var 
content document.createDocumentFragment(), pos 0;
      while (
true) {
        
tokenSpecialChars.lastIndex pos;
        var 
tokenSpecialChars.exec(text);
        var 
skipped m.index pos text.length pos;
        if (
skipped) {
          
content.appendChild(document.createTextNode(text.slice(pospos skipped)));
          
builder.col += skipped;
        }
        if (!
m) break;
        
pos += skipped 1;
        if (
m[0] == "t") {
          var 
tabSize builder.cm.options.tabSizetabWidth tabSize builder.col tabSize;
          
content.appendChild(elt("span"spaceStr(tabWidth), "cm-tab"));
          
builder.col += tabWidth;
        } else {
          var 
token elt("span""u2022""cm-invalidchar");
          
token.title "\u" m[0].charCodeAt(0).toString(16);
          
content.appendChild(token);
          
builder.col += 1;
        }
      }
    }
    if (
style || startStyle || endStyle || builder.measure) {
      var 
fullStyle style || "";
      if (
startStylefullStyle += startStyle;
      if (
endStylefullStyle += endStyle;
      var 
token elt("span", [content], fullStyle);
      if (
titletoken.title title;
      return 
builder.pre.appendChild(token);
    }
    
builder.pre.appendChild(content);
  }

  function 
buildTokenMeasure(buildertextstylestartStyleendStyle) {
    var 
wrapping builder.cm.options.lineWrapping;
    for (var 
0text.length; ++i) {
      var 
ch text.charAt(i), start == 0;
      if (
ch >= "ud800" && ch "udbff" && text.length 1) {
        
ch text.slice(i2);
        ++
i;
      } else if (
&& wrapping && spanAffectsWrapping(texti)) {
        
builder.pre.appendChild(elt("wbr"));
      }
      var 
old builder.measure[builder.pos];
      var 
span builder.measure[builder.pos] =
        
buildToken(builderchstyle,
                   
start && startStyle== text.length && endStyle);
      if (
oldspan.leftSide old.leftSide || old;
      
// In IE single-space nodes wrap differently than spaces
      // embedded in larger text nodes, except when set to
      // white-space: normal (issue #1268).
      
if (ie && wrapping && ch == " " && && !/s/.test(text.charAt(1)) &&
          
text.length && !/s/.test(text.charAt(1)))
        
span.style.whiteSpace "normal";
      
builder.pos += ch.length;
    }
    if (
text.lengthbuilder.measuredSomething true;
  }

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

  function 
buildCollapsedSpan(buildersizemarkerignoreWidget) {
    var 
widget = !ignoreWidget && marker.replacedWith;
    if (
widget) {
      if (
builder.copyWidgetswidget widget.cloneNode(true);
      
builder.pre.appendChild(widget);
      if (
builder.measure) {
        if (
size) {
          
builder.measure[builder.pos] = widget;
        } else {
          var 
elt zeroWidthElement(builder.cm.display.measure);
          if (
marker.type == "bookmark" && !marker.insertLeft)
            
builder.measure[builder.pos] = builder.pre.appendChild(elt);
          else if (
builder.measure[builder.pos])
            return;
          else
            
builder.measure[builder.pos] = builder.pre.insertBefore(eltwidget);
        }
        
builder.measuredSomething true;
      }
    }
    
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]), styleToClass(styles[i+1]));
      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 || collapsed.marker.size m.size))
              
collapsed sp;
          } else if (
sp.from pos && nextChange sp.from) {
            
nextChange sp.from;
          }
          if (
m.type == "bookmark" && sp.from == pos && m.replacedWithfoundBookmarks.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 collapsed.marker.find();
        }
        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 styleToClass(styles[i++]);
      }
    }
  }

  
// DOCUMENT DATA STRUCTURE

  
function updateDoc(docchangemarkedSpansselAfterestimateHeight) {
    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;

    
// First adjust the line structure
    
if (from.ch == && to.ch == && lastText == "") {
      
// This is a whole-line replace. Treated specially to make
      // sure line objects move the way they are supposed to.
      
for (var 0text.length 1added = []; e; ++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 1e; ++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 
1text.length 1added = []; e; ++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);
    
setSelection(docselAfter.anchorselAfter.headnulltrue);
  }

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

  
LeafChunk.prototype = {
    
chunkSize: function() { return this.lines.length; },
    
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);
    },
    
collapse: function(lines) {
      
lines.splice.apply(lines, [lines.length0].concat(this.lines));
    },
    
insertInner: function(atlinesheight) {
      
this.height += height;
      
this.lines this.lines.slice(0at).concat(lines).concat(this.lines.slice(at));
      for (var 
0lines.lengthe; ++ilines[i].parent this;
    },
    
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.lengthe; ++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 (
this.size 25) {
        var 
lines = [];
        
this.collapse(lines);
        
this.children = [new LeafChunk(lines)];
        
this.children[0].parent this;
      }
    },
    
collapse: function(lines) {
      for (var 
0this.children.lengthe; ++ithis.children[i].collapse(lines);
    },
    
insertInner: function(atlinesheight) {
      
this.size += lines.length;
      
this.height += height;
      for (var 
0this.children.lengthe; ++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;
      }
    },
    
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.lengthe; ++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.history makeHistory();
    
this.cleanGeneration 1;
    
this.frontier firstLine;
    var 
start Pos(firstLine0);
    
this.sel = {fromstarttostartheadstartanchorstartshiftfalseextendfalsegoalColumnnull};
    
this.id = ++nextDocId;
    
this.modeOption mode;

    if (
typeof text == "string"text splitLines(text);
    
updateDoc(this, {fromstarttostarttexttext}, null, {headstartanchorstart});
  };

  
Doc.prototype createObj(BranchChunk.prototype, {
    
constructorDoc,
    
iter: function(fromtoop) {
      if (
opthis.iterN(from this.firstto fromop);
      else 
this.iterN(this.firstthis.first this.sizefrom);
    },

    
insert: function(atlines) {
      var 
height 0;
      for (var 
0lines.lengthe; ++iheight += lines[i].height;
      
this.insertInner(at this.firstlinesheight);
    },
    
remove: function(atn) { this.removeInner(at this.firstn); },

    
getValue: function(lineSep) {
      var 
lines getLines(thisthis.firstthis.first this.size);
      if (
lineSep === false) return lines;
      return 
lines.join(lineSep || "n");
    },
    
setValue: function(code) {
      var 
top Pos(this.first0), last this.first this.size 1;
      
makeChange(this, {fromtoptoPos(lastgetLine(thislast).text.length),
                        
textsplitLines(code), origin"setValue"},
                 {
headtopanchortop}, true);
    },
    
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;},
    
setLine: function(linetext) {
      if (
isLine(thisline))
        
replaceRange(thistextPos(line0), clipPos(thisPos(line)));
    },
    
removeLine: function(line) {
      if (
linereplaceRange(this""clipPos(thisPos(line 1)), clipPos(thisPos(line)));
      else 
replaceRange(this""Pos(00), clipPos(thisPos(10)));
    },

    
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(thisline);
    },

    
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 
sel this.selpos;
      if (
start == null || start == "head"pos sel.head;
      else if (
start == "anchor"pos sel.anchor;
      else if (
start == "end" || start === falsepos sel.to;
      else 
pos sel.from;
      return 
copyPos(pos);
    },
    
somethingSelected: function() {return !posEq(this.sel.headthis.sel.anchor);},

    
setCursordocOperation(function(linechextend) {
      var 
pos clipPos(thistypeof line == "number" Pos(linech || 0) : line);
      if (
extendextendSelection(thispos);
      else 
setSelection(thispospos);
    }),
    
setSelectiondocOperation(function(anchorheadbias) {
      
setSelection(thisclipPos(thisanchor), clipPos(thishead || anchor), bias);
    }),
    
extendSelectiondocOperation(function(fromtobias) {
      
extendSelection(thisclipPos(thisfrom), to && clipPos(thisto), bias);
    }),

    
getSelection: function(lineSep) {return this.getRange(this.sel.fromthis.sel.tolineSep);},
    
replaceSelection: function(codecollapseorigin) {
      
makeChange(this, {fromthis.sel.fromtothis.sel.totextsplitLines(code), originorigin}, collapse || "around");
    },
    
undodocOperation(function() {makeChangeFromHistory(this"undo");}),
    
redodocOperation(function() {makeChangeFromHistory(this"redo");}),

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

    
historySize: function() {
      var 
hist this.history;
      return {
undohist.done.lengthredohist.undone.length};
    },
    
clearHistory: function() {this.history makeHistory(this.history.maxGeneration);},

    
markClean: function() {
      
this.cleanGeneration this.changeGeneration();
    },
    
changeGeneration: function() {
      
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 makeHistory(this.history.maxGeneration);
      
hist.done histData.done.slice(0);
      
hist.undone histData.undone.slice(0);
    },

    
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};
      
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;
    },
    
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 = {fromthis.sel.fromtothis.sel.toheadthis.sel.headanchorthis.sel.anchor,
                 
shiftthis.sel.shiftextendfalsegoalColumnthis.sel.goalColumn};
      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}];
      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);
        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 makeHistory();
        
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;}
  });

  
Doc.prototype.eachLine Doc.prototype.iter;

  
// The Doc methods that should be available on CodeMirror instances
  
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);

  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);
  }

  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.lineWrappingcomputeMaxLength(cm);
    
cm.options.mode doc.modeOption;
    
regChange(cm);
  }

  
// LINE UTILITIES

  
function getLine(chunkn) {
    
-= chunk.first;
    while (!
chunk.lines) {
      for (var 
0;; ++i) {
        var 
child chunk.children[i], sz child.chunkSize();
        if (
sz) { chunk child; break; }
        
-= sz;
      }
    }
    return 
chunk.lines[n];
  }

  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;
  }
  function 
getLines(docfromto) {
    var 
out = [];
    
doc.iter(fromto, function(line) { out.push(line.text); });
    return 
out;
  }

  function 
updateLineHeight(lineheight) {
    var 
diff height line.height;
    for (var 
linenn.parentn.height += diff;
  }

  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;
  }

  function 
lineAtHeight(chunkh) {
    var 
chunk.first;
    
outer: do {
      for (var 
0chunk.children.lengthe; ++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.lengthe; ++i) {
      var 
line chunk.lines[i], lh line.height;
      if (
lh) break;
      
-= lh;
    }
    return 
i;
  }

  function 
heightAtLine(cmlineObj) {
    
lineObj visualLine(cm.doclineObj);

    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;
  }

  function 
getOrder(line) {
    var 
order line.order;
    if (
order == nullorder line.order bidiOrdering(line.text);
    return 
order;
  }

  
// HISTORY

  
function makeHistory(startGen) {
    return {
      
// Arrays of history events. Doing something adds an event to
      // done and clears undo. Undoing moves events from done to
      // undone, redoing moves them in the other direction.
      
done: [], undone: [], undoDepthInfinity,
      
// Used to track when changes can be merged into a single undo
      // event
      
lastTime0lastOpnulllastOriginnull,
      
// Used by the isClean() method
      
generationstartGen || 1maxGenerationstartGen || 1
    
};
  }

  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;
    });
  }

  function 
historyChangeFromChange(docchange) {
    var 
from = { linechange.from.linechchange.from.ch };
    var 
histChange = {fromfromtochangeEnd(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;
  }

  function 
addToHistory(docchangeselAfteropId) {
    var 
hist doc.history;
    
hist.undone.length 0;
    var 
time = +new Datecur lst(hist.done);

    if (
cur &&
        (
hist.lastOp == opId ||
         
hist.lastOrigin == change.origin && change.origin &&
         ((
change.origin.charAt(0) == "+" && doc.cm && hist.lastTime time doc.cm.options.historyEventDelay) ||
          
change.origin.charAt(0) == "*"))) {
      
// Merge this change into the last event
      
var last lst(cur.changes);
      if (
posEq(change.fromchange.to) && posEq(change.fromlast.to)) {
        
// 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));
      }
      
cur.anchorAfter selAfter.anchorcur.headAfter selAfter.head;
    } else {
      
// Can not be merged, start a new event.
      
cur = {changes: [historyChangeFromChange(docchange)],
             
generationhist.generation,
             
anchorBeforedoc.sel.anchorheadBeforedoc.sel.head,
             
anchorAfterselAfter.anchorheadAfterselAfter.head};
      
hist.done.push(cur);
      
hist.generation = ++hist.maxGeneration;
      while (
hist.done.length hist.undoDepth)
        
hist.done.shift();
    }
    
hist.lastTime time;
    
hist.lastOp opId;
    
hist.lastOrigin change.origin;
  }

  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;
  }

  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(eventsnewGroup) {
    for (var 
0copy = []; events.length; ++i) {
      var 
event events[i], changes event.changesnewChanges = [];
      
copy.push({changesnewChangesanchorBeforeevent.anchorBeforeheadBeforeevent.headBefore,
                 
anchorAfterevent.anchorAfterheadAfterevent.headAfter});
      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 rebaseHistSel(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;
      for (var 
0sub.changes.length; ++j) {
        var 
cur sub.changes[j];
        if (!
sub.copied) { cur.from copyPos(cur.from); cur.to copyPos(cur.to); }
        if (
to cur.from.line) {
          
cur.from.line += diff;
          
cur.to.line += diff;
        } else if (
from <= cur.to.line) {
          
ok false;
          break;
        }
      }
      if (!
sub.copied) {
        
sub.anchorBefore copyPos(sub.anchorBefore); sub.headBefore copyPos(sub.headBefore);
        
sub.anchorAfter copyPos(sub.anchorAfter); sub.readAfter copyPos(sub.headAfter);
        
sub.copied true;
      }
      if (!
ok) {
        array.
splice(01);
        
0;
      } else {
        
rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
        
rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
      }
    }
  }

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

  
// EVENT OPERATORS

  
function stopMethod() {e_stop(this);}
  
// Ensure an event has a stop method.
  
function addStop(event) {
    if (!
event.stopevent.stop stopMethod;
    return 
event;
  }

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

  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

  
function on(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);
    }
  }

  function 
off(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; }
    }
  }

  function 
signal(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);
  }

  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 
signalDOMEvent(cmeoverride) {
    
signal(cmoverride || e.typecme);
    return 
e_defaultPrevented(e) || e.codemirrorIgnore;
  }

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

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

  
CodeMirror.on onCodeMirror.off offCodeMirror.signal signal;

  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";}};

  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.
  
function countColumn(stringendtabSizestartIndexstartValue) {
    if (
end == null) {
      
end string.search(/[^su00a0]/);
      if (
end == -1end string.length;
    }
    for (var 
startIndex || 0startValue || 0end; ++i) {
      if (
string.charAt(i) == "t"+= tabSize - (tabSize);
      else ++
n;
    }
    return 
n;
  }
  
CodeMirror.countColumn countColumn;

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

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

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

  function 
indexOf(collectionelt) {
    if (
collection.indexOf) return collection.indexOf(elt);
    for (var 
0collection.lengthe; ++i)
      if (
collection[i] == elt) return i;
    return -
1;
  }

  function 
createObj(baseprops) {
    function 
Obj() {}
    
Obj.prototype base;
    var 
inst = new Obj();
    if (
propscopyObj(propsinst);
    return 
inst;
  }

  function 
copyObj(objtarget) {
    if (!
targettarget = {};
    for (var 
prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
    return 
target;
  }

  function 
emptyArray(size) {
    for (var 
= [], 0size; ++ia.push(undefined);
    return 
a;
  }

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

  var 
nonASCIISingleCaseWordChar = /[u3040-u309fu30a0-u30ffu3400-u4db5u4e00-u9fccuac00-ud7af]/;
  function 
isWordChar(ch) {
    return /
w/.test(ch) || ch "x80" &&
      (
ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
  }

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

  var 
isExtendingChar = /[u0300-u036Fu0483-u0487u0488-u0489u0591-u05BDu05BFu05C1-u05C2u05C4-u05C5u05C7u0610-u061Au064B-u065Fu0670u06D6-u06DCu06DF-u06E4u06E7-u06E8u06EA-u06EDuA66FuA670-uA672uA674-uA67DuA69Fudc00-udfff]/;

  
// DOM UTILITIES

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

  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 
setTextContent(estr) {
    if (
ie_lt9) {
      
e.innerHTML "";
      
e.appendChild(document.createTextNode(str));
    } else 
e.textContent str;
  }

  function 
getRect(node) {
    return 
node.getBoundingClientRect();
  }
  
CodeMirror.replaceGetRect = function(f) { getRect f; };

  
// 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_lt9) return false;
    var 
div elt('div');
    return 
"draggable" in div || "dragDrop" in div;
  }();

  
// For a reason I have yet to figure out, some browsers disallow
  // word wrapping between certain characters *only* if a new inline
  // element is started between them. This makes it hard to reliably
  // measure the position of things, since that requires inserting an
  // extra span. This terribly fragile set of tests matches the
  // character combinations that suffer from this phenomenon on the
  // various browsers.
  
function spanAffectsWrapping() { return false; }
  if (
gecko// Only for "$'"
    
spanAffectsWrapping = function(stri) {
      return 
str.charCodeAt(1) == 36 && str.charCodeAt(i) == 39;
    };
  else if (
safari && !/Version/([6-9]|dd)b/.test(navigator.userAgent))
    
spanAffectsWrapping = function(stri) {
      return /-[^ -?]|?[^ !
'"),.-/:;?]}]/.test(str.slice(i - 1, i + 1));
    };
  else if (webkit && !/Chrome/(?:29|[3-9]d|ddd)./.test(navigator.userAgent))
    spanAffectsWrapping = function(str, i) {
      if (i > 1 && str.charCodeAt(i - 1) == 45) {
        if (/w/.test(str.charAt(i - 2)) && /[^-?.]/.test(str.charAt(i))) return true;
        if (i > 2 && /[d.,]/.test(str.charAt(i - 2)) && /[d.,]/.test(str.charAt(i))) return false;
      }
      return /[~!#%&*)=+}]|".>,:;][({[<]|-[^-?.u2010-u201fu2026]|?[w~`@#$%^&*(_=+{[|><]|…[w~`@#$%^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
    };

  var knownScrollbarWidth;
  function scrollbarWidth(measure) {
    if (knownScrollbarWidth != null) return knownScrollbarWidth;
    var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
    removeChildrenAndAdd(measure, test);
    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(measure, elt("span", [test, document.createTextNode("x")]));
      if (measure.firstChild.offsetHeight != 0)
        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
    }
    if (zwspSupported) return elt("span", "u200b");
    else return elt("span", "u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
  }

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

  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 e = elt("div");
    if ("oncopy" in e) return true;
    e.setAttribute("oncopy", "return;");
    return typeof e.oncopy == '
function';
  })();

  // KEY NAMING

  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: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
                  186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\",
                  221: "]", 222: "'", 63276: "
PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
                  63234: "
Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
  CodeMirror.keyNames = keyNames;
  (function() {
    // Number keys
    for (var i = 0; i < 10; i++) keyNames[i + 48] = 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(cm.doc, 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;
    while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
      lineN = merged.find().to.line;
    var order = getOrder(line);
    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
    return Pos(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) {
    for (var i = 0, found; i < order.length; ++i) {
      var cur = order[i];
      if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
      if (cur.from == pos || cur.to == pos) {
        if (found == null) {
          found = i;
        } else if (compareBidiLevel(order, cur.level, order[found].level)) {
          bidiOther = found;
          return i;
        } else {
          bidiOther = i;
          return found;
        }
      }
    }
    bidiOther = null;
    return found;
  }

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

  // This is somewhat involved. It 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.test(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%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
    // Character types for codepoints 0x600 to 0x6ff
    var arabicTypes = "
rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
    function charType(code) {
      if (code <= 0xff) return lowTypes.charAt(code);
      else if (0x590 <= code && code <= 0x5f4) return "
R";
      else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
      else if (0x700 <= code && code <= 0x8ac) return "
r";
      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";

    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 - 1 && 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 - 1 ? 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 i = 0; i < len;) {
        if (countsAsLeft.test(types[i])) {
          var start = i;
          for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
          order.push({from: start, to: i, level: 0});
        } else {
          var pos = i, at = order.length;
          for (++i; i < len && types[i] != "
L"; ++i) {}
          for (var j = pos; j < i;) {
            if (countsAsNum.test(types[j])) {
              if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
              var nstart = j;
              for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
              order.splice(at, 0, {from: nstart, to: j, level: 2});
              pos = j;
            } else ++j;
          }
          if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
        }
      }
      if (order[0].level == 1 && (m = str.match(/^s+/))) {
        order[0].from = m[0].length;
        order.unshift({from: 0, to: m[0].length, level: 0});
      }
      if (lst(order).level == 1 && (m = str.match(/s+$/))) {
        lst(order).to -= m[0].length;
        order.push({from: len - m[0].length, to: len, level: 0});
      }
      if (order[0].level != lst(order).level)
        order.push({from: len, to: len, level: order[0].level});

      return order;
    };
  })();

  // THE END

  CodeMirror.version = "
3.16.0";

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