Вход Регистрация
Файл: upload/admin/view/javascript/ckeditor/plugins/codemirror/js/codemirror.js
Строк: 7448
<?php
// CodeMirror version 3.0
//
// 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 = /MSIE [1-7]b/.test(navigator.userAgent);
  var 
ie_lt9 = /MSIE [1-8]b/.test(navigator.userAgent);
  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|IEMobile/i.test(navigator.userAgent);
  var 
mac ios || /Mac/.test(navigator.platform);

  
// 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 
display this.display makeDisplay(place);
    
display.wrapper.CodeMirror this;
    
updateGutters(this);
    if (
options.autofocus && !mobilefocusInput(this);

    
this.view makeView(new BranchChunk([new LeafChunk([makeLine(""nulltextHeight(display))])]));
    
this.nextOpId 0;
    
loadMode(this);
    
themeChanged(this);
    if (
options.lineWrapping)
      
this.display.wrapper.className += " CodeMirror-wrap";

    
// Initialize the content.
    
this.setValue(options.value || "");
    
// Override magic textarea content restore that IE sometimes does
    // on our hidden textarea on reload
    
if (iesetTimeout(bind(resetInputthistrue), 20);
    
this.view.history makeHistory();

    
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(place) {
    var 
= {};
    var 
input d.input elt("textarea"nullnull"position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
    
input.setAttribute("wrap""off"); input.setAttribute("autocorrect""off"); input.setAttribute("autocapitalize""off");
    
// 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");
    
// DIVs containing the selection and the actual code
    
d.lineDiv elt("div");
    
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("pre""u00a0""CodeMirror-cursor");
    
// Secondary cursor, shown when on a 'jump' in bi-directional text
    
d.otherCursor elt("pre""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""u00a0"null"position: absolute; height: " scrollerCutOff "px");
    
// Will contain the gutters, if any
    
d.gutters elt("div"null"CodeMirror-gutters");
    
d.lineGutter null;
    
// Helper element to properly size the gutter backgrounds
    
var scrollerInner elt("div", [d.sizerd.heightForcerd.gutters], null"position: relative; min-height: 100%");
    
// Provides scrolling
    
d.scroller elt("div", [scrollerInner], "CodeMirror-scroll");
    
d.scroller.setAttribute("tabIndex""-1");
    
// The element in which the editor lives.
    
d.wrapper elt("div", [d.inputDivd.scrollbarHd.scrollbarV,
                            
d.scrollbarFillerd.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.showingFrom d.showingTo d.lastSizeC 0;

    
// 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();
    
// True when a drag from the editor is active
    
d.draggingText false;

    
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;

    
// Used to adjust overwrite behaviour when a paste has been
    // detected
    
d.pasteIncoming false;

    return 
d;
  }

  
// VIEW CONSTRUCTOR

  
function makeView(doc) {
    var 
selPos = {line0ch0};
    return {
      
docdoc,
      
// frontier is the point up to which the content has been parsed,
      
frontier0highlight: new Delayed(),
      
sel: {fromselPostoselPosheadselPosanchorselPosshiftfalseextendfalse},
      
scrollTop0scrollLeft0,
      
overwritefalsefocusedfalse,
      
// Tracks the maximum line length so that
      // the horizontal scrollbar can be kept
      // static when scrolling.
      
maxLinegetLine(doc0),
      
maxLineLength0,
      
maxLineChangedfalse,
      
suppressEditsfalse,
      
goalColumnnull,
      
cantEditfalse,
      
keyMaps: []
    };
  }

  
// STATE UPDATES

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

  
function loadMode(cm) {
    var 
doc cm.view.doc;
    
cm.view.mode CodeMirror.getMode(cm.optionscm.options.mode);
    
doc.iter(0doc.size, function(line) { line.stateAfter null; });
    
cm.view.frontier 0;
    
startWorker(cm100);
  }

  function 
wrappingChanged(cm) {
    var 
doc cm.view.docth textHeight(cm.display);
    if (
cm.options.lineWrapping) {
      
cm.display.wrapper.className += " CodeMirror-wrap";
      var 
perLine cm.display.scroller.clientWidth charWidth(cm.display) - 3;
      
doc.iter(0doc.size, function(line) {
        if (
line.height == 0) return;
        var 
guess Math.ceil(line.text.length perLine) || 1;
        if (
guess != 1updateLineHeight(lineguess th);
      });
      
cm.display.sizer.style.minWidth "";
    } else {
      
cm.display.wrapper.className cm.display.wrapper.className.replace(" CodeMirror-wrap""");
      
computeMaxLength(cm.view);
      
doc.iter(0doc.size, function(line) {
        if (
line.height != 0updateLineHeight(lineth);
      });
    }
    
regChange(cm0doc.size);
    
clearCaches(cm);
    
setTimeout(function(){updateScrollbars(cm.displaycm.view.doc.height);}, 100);
  }

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

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

  function 
guttersChanged(cm) {
    
updateGutters(cm);
    
updateDisplay(cmtrue);
  }

  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(view) {
    
view.maxLine getLine(view.doc0);
    
view.maxLineLength lineLength(view.docview.maxLine);
    
view.maxLineChanged true;
    
view.doc.iter(1view.doc.size, function(line) {
      var 
len lineLength(view.docline);
      if (
len view.maxLineLength) {
        
view.maxLineLength len;
        
view.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(/* display */docHeight) {
    var 
totalHeight docHeight paddingTop(d);
    
d.sizer.style.minHeight d.heightForcer.style.top totalHeight "px";
    var 
scrollHeight Math.max(totalHeightd.scroller.scrollHeight);
    var 
needsH d.scroller.scrollWidth d.scroller.clientWidth;
    var 
needsV scrollHeight d.scroller.clientHeight;
    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 "";
    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 "";
    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 (
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) return;
    var 
comp compensateForHScroll(display) - display.scroller.scrollLeft cm.view.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;
    }
    
display.gutters.style.left = (comp gutterW) + "px";
  }

  function 
maybeUpdateLineNumberWidth(cm) {
    if (!
cm.options.lineNumbers) return false;
    var 
doc cm.view.doclast lineNumberFor(cm.optionsdoc.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 
display.scroller.getBoundingClientRect().left display.sizer.getBoundingClientRect().left;
  }

  
// DISPLAY DRAWING

  
function updateDisplay(cmchangesviewPort) {
    var 
oldFrom cm.display.showingFromoldTo cm.display.showingTo;
    var 
updated updateDisplayInner(cmchangesviewPort);
    if (
updated) {
      
signalLater(cmcm"update"cm);
      if (
cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
        
signalLater(cmcm"viewportChange"cmcm.display.showingFromcm.display.showingTo);
    }
    
updateSelection(cm);
    
updateScrollbars(cm.displaycm.view.doc.height);

    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(cmchangesviewPort) {
    var 
display cm.displaydoc cm.view.doc;
    if (!
display.wrapper.clientWidth) {
      
display.showingFrom display.showingTo display.viewOffset 0;
      return;
    }

    
// Compute the new visible window
    // If scrollTop is specified, use that to determine which lines
    // to render instead of the current scrollbar position.
    
var visible visibleLines(displaydocviewPort);
    
// Bail out if the visible area is already rendered and nothing changed.
    
if (changes !== true && changes.length == &&
        
visible.from display.showingFrom && visible.to display.showingTo)
      return;

    if (
changes && maybeUpdateLineNumberWidth(cm))
      
changes true;
    
display.sizer.style.marginLeft display.scrollbarH.style.left display.gutters.offsetWidth "px";

    
// When merged lines are present, the line that needs to be
    // redrawn might not be the one that was changed.
    
if (changes !== true && sawCollapsedSpans)
      for (var 
0changes.length; ++i) {
        var 
ch changes[i], merged;
        while (
merged collapsedSpanAtStart(getLine(docch.from))) {
          var 
from merged.find().from.line;
          if (
ch.diffch.diff -= ch.from from;
          
ch.from from;
        }
      }

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

    var 
from Math.max(visible.from cm.options.viewportMargin0);
    var 
to Math.min(doc.sizevisible.to cm.options.viewportMargin);
    if (
display.showingFrom from && from display.showingFrom 20from display.showingFrom;
    if (
display.showingTo to && display.showingTo to 20to Math.min(doc.sizedisplay.showingTo);
    if (
sawCollapsedSpans) {
      
from lineNo(visualLine(docgetLine(docfrom)));
      while (
to doc.size && lineIsHidden(getLine(docto))) ++to;
    }

    
// Create a range of theoretically intact lines, and punch holes
    // in that using the change info.
    
var intact changes === true ? [] :
      
computeIntact([{fromdisplay.showingFromtodisplay.showingTo}], changes);
    
// 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 (
intactLines == to from && from == display.showingFrom && to == display.showingTo)
      return;
    
intact.sort(function(ab) {return a.from b.from;});

    if (
intactLines < (to from) * .7display.lineDiv.style.display "none";
    
patchDisplay(cmfromtointactpositionsChangedFrom);
    
display.lineDiv.style.display "";

    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 (differentdisplay.lastSizeC display.wrapper.clientHeight;
    
display.showingFrom fromdisplay.showingTo to;
    
startWorker(cm100);

    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 node.getBoundingClientRect();
        
height box.bottom box.top;
      }
      var 
diff node.lineObj.height height;
      if (
height 2height textHeight(display);
      if (
diff .001 || diff < -.001)
        
updateLineHeight(node.lineObjheight);
    }
    
display.viewOffset heightAtLine(cmgetLine(docfrom));
    
// Position the mover div to align with the current virtual scroll position
    
display.mover.style.top display.viewOffset "px";
    return 
true;
  }

  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;
    
// IE does bad things to nodes when .innerHTML = "" is used on a parent
    // we still need widgets and markers intact to add back to the new content later
    
if (!intact.length && !ie && (!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 {
        
container.removeChild(node);
      }
      return 
next;
    }

    var 
nextIntact intact.shift(), lineNo from;
    
cm.view.doc.iter(fromto, function(line) {
      if (
nextIntact && nextIntact.to == lineNonextIntact intact.shift();
      if (
lineIsHidden(line)) {
        if (
line.height != 0updateLineHeight(line0);
      } else if (
nextIntact && nextIntact.from <= lineNo && nextIntact.to lineNo) {
        
// This line is intact. Skip to the actual node. Update its
        // line number if needed.
        
while (cur.lineObj != linecur rm(cur);
        if (
lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
          
setTextContent(cur.lineNumberlineNumberFor(cm.optionslineNo));
        
cur cur.nextSibling;
      } else {
        
// This line needs to be generated.
        
var lineNode buildLineElement(cmlinelineNodims);
        
container.insertBefore(lineNodecur);
        
lineNode.lineObj line;
      }
      ++
lineNo;
    });
    while (
curcur rm(cur);
  }

  function 
buildLineElement(cmlinelineNodims) {
    var 
lineElement lineContent(cmline);
    var 
markers line.gutterMarkersdisplay cm.display;

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

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

    
var wrap elt("div"nullline.wrapClass"position: relative");
    if (
cm.options.lineNumbers || markers) {
      var 
gutterWrap wrap.appendChild(elt("div"nullnull"position: absolute; left: " +
                                            
dims.fixedPos "px"));
      
wrap.alignable = [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"));
        }
    }
    
// Kludge to make sure the styled element lies behind the selection (by z-index)
    
if (line.bgClass)
      
wrap.appendChild(elt("div""u00a0"line.bgClass " CodeMirror-linebackground"));
    
wrap.appendChild(lineElement);
    if (
line.widgets)
      for (var 
0ws line.widgetsws.length; ++i) {
        var 
widget ws[i], node elt("div", [widget.node], "CodeMirror-linewidget");
        
node.widget widget;
        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";
        }
        if (
widget.above)
          
wrap.insertBefore(nodecm.options.lineNumbers && line.height != gutterWrap lineElement);
        else
          
wrap.appendChild(node);
      }

    if (
ie_lt8wrap.style.zIndex 2;
    return 
wrap;
  }

  
// SELECTION / CURSOR

  
function updateSelection(cm) {
    var 
display cm.display;
    var 
collapsed posEq(cm.view.sel.fromcm.view.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
    
var headPos cursorCoords(cmcm.view.sel.head"div");
    var 
wrapOff display.wrapper.getBoundingClientRect(), lineOff display.lineDiv.getBoundingClientRect();
    
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.view.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.view.docsel cm.view.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(linefromArgtoArgretTop) {
      var 
lineObj getLine(docline);
      var 
lineLen lineObj.text.lengthrVal retTop Infinity : -Infinity;
      function 
coords(ch) {
        return 
charCoords(cm, {linelinechch}, "div"lineObj);
      }

      
iterateBidiSections(getOrder(lineObj), fromArg || 0toArg == null lineLen toArg, function(fromtodir) {
        var 
leftPos coords(dir == "rtl" to from);
        var 
rightPos coords(dir == "rtl" from to 1);
        var 
left leftPos.leftright rightPos.right;
        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 (
fromArg == null && from == 0left pl;
        
rVal retTop Math.min(rightPos.toprVal) : Math.max(rightPos.bottomrVal);
        if (
left pl 1left pl;
        
add(leftrightPos.topright leftrightPos.bottom);
      });
      return 
rVal;
    }

    if (
sel.from.line == sel.to.line) {
      
drawForLine(sel.from.linesel.from.chsel.to.ch);
    } else {
      var 
fromObj getLine(docsel.from.line);
      var 
cur fromObjmergedpath = [sel.from.linesel.from.ch], singleLine;
      while (
merged collapsedSpanAtEnd(cur)) {
        var 
found merged.find();
        
path.push(found.from.chfound.to.linefound.to.ch);
        if (
found.to.line == sel.to.line) {
          
path.push(sel.to.ch);
          
singleLine true;
          break;
        }
        
cur getLine(docfound.to.line);
      }

      
// This is a single, merged line
      
if (singleLine) {
        for (var 
0path.length+= 3)
          
drawForLine(path[i], path[i+1], path[i+2]);
      } else {
        var 
middleTopmiddleBottoObj getLine(docsel.to.line);
        if (
sel.from.ch)
          
// Draw the first line of selection.
          
middleTop drawForLine(sel.from.linesel.from.chnullfalse);
        else
          
// Simply include it in the middle block.
          
middleTop heightAtLine(cmfromObj) - display.viewOffset;

        if (!
sel.to.ch)
          
middleBot heightAtLine(cmtoObj) - display.viewOffset;
        else
          
middleBot drawForLine(sel.to.linecollapsedSpanAtStart(toObj) ? null 0sel.to.chtrue);

        if (
middleTop middleBotadd(plmiddleTopnullmiddleBot);
      }
    }

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

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

  
// HIGHLIGHT WORKER

  
function startWorker(cmtime) {
    if (
cm.view.frontier cm.display.showingTo)
      
cm.view.highlight.set(timebind(highlightWorkercm));
  }

  function 
highlightWorker(cm) {
    var 
view cm.viewdoc view.doc;
    if (
view.frontier >= cm.display.showingTo) return;
    var 
end = +new Date cm.options.workTime;
    var 
state copyState(view.modegetStateBefore(cmview.frontier));
    var 
changed = [], prevChange;
    
doc.iter(view.frontierMath.min(doc.sizecm.display.showingTo 500), function(line) {
      if (
view.frontier >= cm.display.showingFrom) { // Visible
        
if (highlightLine(cmlinestate) && view.frontier >= cm.display.showingFrom) {
          if (
prevChange && prevChange.end == view.frontierprevChange.end++;
          else 
changed.push(prevChange = {startview.frontierendview.frontier 1});
        }
        
line.stateAfter copyState(view.modestate);
      } else {
        
processLine(cmlinestate);
        
line.stateAfter view.frontier == copyState(view.modestate) : null;
      }
      ++
view.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(cmn) {
    var 
minindentminlinedoc cm.view.doc;
    for (var 
search nlim 100search lim; --search) {
      if (
search == 0) return 0;
      var 
line getLine(docsearch-1);
      if (
line.stateAfter) return search;
      var 
indented countColumn(line.textnullcm.options.tabSize);
      if (
minline == null || minindent indented) {
        
minline search 1;
        
minindent indented;
      }
    }
    return 
minline;
  }

  function 
getStateBefore(cmn) {
    var 
view cm.view;
    var 
pos findStartLine(cmn), state pos && getLine(view.docpos-1).stateAfter;
    if (!
statestate startState(view.mode);
    else 
state copyState(view.modestate);
    
view.doc.iter(posn, function(line) {
      
processLine(cmlinestate);
      var 
save pos == || pos == || pos >= view.showingFrom && pos view.showingTo;
      
line.stateAfter save copyState(view.modestate) : null;
      ++
pos;
    });
    return 
state;
  }

  
// POSITION MEASUREMENT
  
  
function paddingTop(display) {return display.lineSpace.offsetTop;}
  function 
paddingLeft(display) {
    var 
removeChildrenAndAdd(display.measureelt("pre")).appendChild(elt("span""x"));
    return 
e.offsetLeft;
  }

  function 
measureChar(cmlinechdata) {
    var 
data data || measureLine(cmline), dir = -1;
    for (var 
pos ch;; pos += dir) {
      var 
data[pos];
      if (
r) break;
      if (
dir && pos == 0dir 1;
    }
    return {
leftpos ch r.right r.left,
            
rightpos ch r.left r.right,
            
topr.topbottomr.bottom};
  }

  function 
measureLine(cmline) {
    
// First look in the cache
    
var display cm.displaycache cm.display.measureLineCache;
    for (var 
0cache.length; ++i) {
      var 
memo cache[i];
      if (
memo.text == line.text && memo.markedSpans == line.markedSpans &&
          
display.scroller.clientWidth == memo.width)
        return 
memo.measure;
    }
    
    var 
measure measureLineInner(cmline);
    
// Store result in the cache
    
var memo = {textline.textwidthdisplay.scroller.clientWidth,
                
markedSpansline.markedSpansmeasuremeasure};
    if (
cache.length == 16cache[++display.measureLineCachePos 16] = memo;
    else 
cache.push(memo);
    return 
measure;
  }

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

    
// 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 display.lineDiv.getBoundingClientRect();
    var 
vranges = [], data emptyArray(line.text.length), maxBot pre.offsetHeight;
    for (var 
0curmeasure.length; ++i) if (cur measure[i]) {
      var 
size cur.getBoundingClientRect();
      var 
top Math.max(0size.top outer.top), bot Math.min(size.bottom outer.topmaxBot);
      for (var 
0vranges.length+= 2) {
        var 
rtop vranges[j], rbot vranges[j+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[j] = Math.min(toprtop);
          
vranges[j+1] = Math.max(botrbot);
          break;
        }
      }
      if (
== vranges.lengthvranges.push(topbot);
      
data[i] = {leftsize.left outer.leftrightsize.right outer.lefttopj};
    }
    for (var 
0curdata.length; ++i) if (cur data[i]) {
      var 
vr cur.top;
      
cur.top vranges[vr]; cur.bottom vranges[vr+1];
    }
    return 
data;
  }

  function 
clearCaches(cm) {
    
cm.display.measureLineCache.length cm.display.measureLineCachePos 0;
    
cm.display.cachedCharWidth cm.display.cachedTextHeight null;
    
cm.view.maxLineChanged true;
  }

  
// 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 lineObj.widgets[i].node.offsetHeight;
      
rect.top += sizerect.bottom += size;
    }
    if (
context == "line") return rect;
    if (!
contextcontext "local";
    var 
yOff heightAtLine(cmlineObj);
    if (
context != "local"yOff -= cm.display.viewOffset;
    if (
context == "page") {
      var 
lOff cm.display.lineSpace.getBoundingClientRect();
      
yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
      var 
xOff lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
      
rect.left += xOffrect.right += xOff;
    }
    
rect.top += yOffrect.bottom += yOff;
    return 
rect;
  }

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

  function 
cursorCoords(cmposcontextlineObjmeasurement) {
    
lineObj lineObj || getLine(cm.view.docpos.line);
    if (!
measurementmeasurement measureLine(cmlineObj);
    function 
get(chright) {
      var 
measureChar(cmlineObjchmeasurement);
      if (
rightm.left m.right; else m.right m.left;
      return 
intoCoordSystem(cmlineObjmcontext);
    }
    var 
order getOrder(lineObj), ch pos.ch;
    if (!
order) return get(ch);
    var 
mainotherlinedir order[0].level;
    for (var 
0order.length; ++i) {
      var 
part order[i], rtl part.level 2nbhere;
      if (
part.from ch && part.to ch) return get(chrtl);
      var 
left rtl part.to part.fromright rtl part.from part.to;
      if (
left == ch) {
        
// Opera and IE return bogus offsets and widths for edges
        // where the direction flips, but only for the side with the
        // lower level. So we try to use the side with the higher
        // level.
        
if (&& part.level < (nb order[i-1]).levelhere get(nb.level nb.from nb.to 1true);
        else 
here get(rtl && part.from != part.to ch ch);
        if (
rtl == linedirmain here; else other here;
      } else if (
right == ch) {
        var 
nb order.length && order[i+1];
        if (!
rtl && nb && nb.from == nb.to) continue;
        if (
nb && part.level nb.levelhere get(nb.level nb.to nb.from);
        else 
here get(rtl ch ch 1true);
        if (
rtl == linedirmain here; else other here;
      }
    }
    if (
linedir && !chother get(order[0].to 1);
    if (!
main) return other;
    if (
othermain.other other;
    return 
main;
  }

  
// Coords must be lineSpace-local
  
function coordsChar(cmxy) {
    var 
doc cm.view.doc;
    
+= cm.display.viewOffset;
    if (
0) return {line0ch0outsidetrue};
    var 
lineNo lineAtHeight(docy);
    if (
lineNo >= doc.size) return {linedoc.size 1chgetLine(docdoc.size 1).text.length};
    if (
00;

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

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

    function 
getX(ch) {
      var 
sp cursorCoords(cm, {linelineNochch}, "line",
                            
lineObjmeasurement);
      
wrongLine true;
      if (
innerOff sp.bottom) return Math.max(0sp.left cWidth);
      else if (
innerOff sp.top) return sp.left cWidth;
      else 
wrongLine false;
      return 
sp.left;
    }

    var 
bidi getOrder(lineObj), dist lineObj.text.length;
    var 
from lineLeft(lineObj), to lineRight(lineObj);
    var 
fromX paddingLeft(cm.display), toX getX(to);

    if (
toX) return {linelineNochtooutsidewrongLine};
    
// Do a binary search between these bounds.
    
for (;;) {
      if (
bidi to == from || to == moveVisually(lineObjfrom1) : to from <= 1) {
        var 
after fromX toX xch after from to;
        while (
isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
        return {
linelineNochchafterafteroutsidewrongLine};
      }
      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 (wrongLinetoX += 1000dist -= step;}
      else {
from middlefromX middleXdist 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.

  
function startOperation(cm) {
    if (
cm.curOp) ++cm.curOp.depth;
    else 
cm.curOp = {
      
// Nested operations delay update until the outermost one
      // finishes.
      
depth1,
      
// An array of ranges of lines that have to be updated. See
      // updateDisplay.
      
changes: [],
      
delayedCallbacks: [],
      
updateInputnull,
      
userSelChangenull,
      
textChangednull,
      
selectionChangedfalse,
      
updateMaxLinefalse,
      
id: ++cm.nextOpId
    
};
  }

  function 
endOperation(cm) {
    var 
op cm.curOp;
    if (--
op.depth) return;
    
cm.curOp null;
    var 
view cm.viewdisplay cm.display;
    if (
op.updateMaxLinecomputeMaxLength(view);
    if (
view.maxLineChanged && !cm.options.lineWrapping) {
      var 
width measureChar(cmview.maxLineview.maxLine.text.length).right;
      
display.sizer.style.minWidth = (width scrollerCutOff) + "px";
      
view.maxLineChanged false;
    }
    var 
newScrollPosupdated;
    if (
op.selectionChanged) {
      var 
coords cursorCoords(cmview.sel.head);
      
newScrollPos calculateScrollPos(cmcoords.leftcoords.topcoords.leftcoords.bottom);
    }
    if (
op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
      
updated updateDisplay(cmop.changesnewScrollPos && newScrollPos.scrollTop);
    if (!
updated && op.selectionChangedupdateSelection(cm);
    if (
newScrollPosscrollCursorIntoView(cm);
    if (
op.selectionChangedrestartBlink(cm);

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

    if (
op.textChanged)
      
signal(cm"change"cmop.textChanged);
    if (
op.selectionChangedsignal(cm"cursorActivity"cm);
    for (var 
0op.delayedCallbacks.length; ++iop.delayedCallbacks[i](cm);
  }

  
// Wraps a function in an operation. Returns the wrapped function.
  
function operation(cm1f) {
    return function() {
      var 
cm cm1 || this;
      
startOperation(cm);
      try {var 
result f.apply(cmarguments);}
      finally {
endOperation(cm);}
      return 
result;
    };
  }

  function 
regChange(cmfromtolendiff) {
    
cm.curOp.changes.push({fromfromtotodifflendiff});
  }

  
// INPUT HANDLING

  
function slowPoll(cm) {
    if (
cm.view.pollingFast) return;
    
cm.display.poll.set(cm.options.pollInterval, function() {
      
readInput(cm);
      if (
cm.view.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.prevInputview cm.viewsel view.sel;
    if (!
view.focused || hasSelection(input) || isReadOnly(cm)) return false;
    var 
text input.value;
    if (
text == prevInput && posEq(sel.fromsel.to)) return false;
    
startOperation(cm);
    
view.sel.shift false;
    var 
same 0Math.min(prevInput.lengthtext.length);
    while (
same && prevInput[same] == text[same]) ++same;
    var 
from sel.fromto sel.to;
    if (
same prevInput.length)
      
from = {linefrom.linechfrom.ch - (prevInput.length same)};
    else if (
view.overwrite && posEq(fromto) && !cm.display.pasteIncoming)
      
to = {lineto.linechMath.min(getLine(cm.view.docto.line).text.lengthto.ch + (text.length same))};
    var 
updateInput cm.curOp.updateInput;
    
updateDoc(cmfromtosplitLines(text.slice(same)), "end",
              
cm.display.pasteIncoming "paste" "input", {fromfromtoto});
    
cm.curOp.updateInput updateInput;
    if (
text.length 1000input.value cm.display.prevInput "";
    else 
cm.display.prevInput text;
    
endOperation(cm);
    
cm.display.pasteIncoming false;
    return 
true;
  }

  function 
resetInput(cmuser) {
    var 
view cm.viewminimalselected;
    if (!
posEq(view.sel.fromview.sel.to)) {
      
cm.display.prevInput "";
      
minimal hasCopyEvent &&
        (
view.sel.to.line view.sel.from.line 100 || (selected cm.getSelection()).length 1000);
      if (
minimalcm.display.input.value "-";
      else 
cm.display.input.value selected || cm.getSelection();
      if (
view.focusedselectInput(cm.display.input);
    } else if (
usercm.display.prevInput cm.display.input.value "";
    
cm.display.inaccurateSelection minimal;
  }

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

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

  
// EVENT HANDLERS

  
function registerEventHandlers(cm) {
    var 
cm.display;
    
on(d.scroller"mousedown"operation(cmonMouseDown));
    
on(d.scroller"dblclick"operation(cme_preventDefault));
    
on(d.lineSpace"selectstart", function(e) {
      if (!
mouseEventInWidget(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 (!geckoon(d.scroller"contextmenu", function(e) {onContextMenu(cme);});

    
on(d.scroller"scroll", function() {
      
setScrollTop(cmd.scroller.scrollTop);
      
setScrollLeft(cmd.scroller.scrollLefttrue);
      
signal(cm"scroll"cm);
    });
    
on(d.scrollbarV"scroll", function() {
      
setScrollTop(cmd.scrollbarV.scrollTop);
    });
    
on(d.scrollbarH"scroll", function() {
      
setScrollLeft(cmd.scrollbarH.scrollLeft);
    });

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

    function 
reFocus() { if (cm.view.focusedsetTimeout(bind(focusInputcm), 0); }
    
on(d.scrollbarH"mousedown"reFocus);
    
on(d.scrollbarV"mousedown"reFocus);
    
// Prevent wrapper from ever scrolling
    
on(d.wrapper"scroll", function() { d.wrapper.scrollTop d.wrapper.scrollLeft 0; });
    
on(window"resize", function resizeHandler() {
      
// Might be a text scaling operation, clear size caches.
      
d.cachedCharWidth d.cachedTextHeight null;
      
clearCaches(cm);
      if (
d.wrapper.parentNodeupdateDisplay(cmtrue);
      else 
off(window"resize"resizeHandler);
    });

    
on(d.input"keyup"operation(cm, function(e) {
      if (
cm.options.onKeyEvent && cm.options.onKeyEvent(cmaddStop(e))) return;
      if (
e_prop(e"keyCode") == 16cm.view.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 (
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(){focusInput(cm); fastPoll(cm);});
    
on(d.input"paste", function() {
      
d.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 
mouseEventInWidget(displaye) {
    for (var 
e_target(e); != display.wrappern.parentNode)
      if (/
bCodeMirror-(?:line)?widgetb/.test(n.className) ||
          
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) return null;
    }
    var 
xyspace display.lineSpace.getBoundingClientRect();
    
// 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) {
    var 
cm thisdisplay cm.displayview cm.viewsel view.seldoc view.doc;
    
sel.shift e_prop(e"shiftKey");

    if (
mouseEventInWidget(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 (
geckoonContextMenu.call(cmcme);
      return;
    case 
2:
      if (
startextendSelection(cmstart);
      
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 (!
view.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(cmword.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;
        
view.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(cmstart);
          
focusInput(cm);
        }
      });
      
// Let the drag handler handle this.
      
if (webkitdisplay.scroller.draggable true;
      
view.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(cmclipPos(docstart));

    var 
startstart sel.fromstartend sel.to;

    function 
doSelect(cur) {
      if (
type == "single") {
        
extendSelection(cmclipPos(docstart), cur);
        return;
      }

      
startstart clipPos(docstartstart);
      
startend clipPos(docstartend);
      if (
type == "double") {
        var 
word findWordAt(getLine(doccur.line).textcur);
        if (
posLess(curstartstart)) extendSelection(cmword.fromstartend);
        else 
extendSelection(cmstartstartword.to);
      } else if (
type == "triple") {
        if (
posLess(curstartstart)) extendSelection(cmstartendclipPos(doc, {linecur.linech0}));
        else 
extendSelection(cmstartstartclipPos(doc, {linecur.line 1ch0}));
      }
    }

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

    function 
extend(e) {
      var 
curCount = ++counter;
      var 
cur posFromMouse(cmetrue);
      if (!
cur) return;
      if (!
posEq(curlast)) {
        if (!
view.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;
      var 
cur posFromMouse(cme);
      if (
curdoSelect(cur);
      
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 
onDrop(e) {
    var 
cm this;
    if (
cm.options.onDragEvent && cm.options.onDragEvent(cmaddStop(e))) return;
    
e_preventDefault(e);
    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.view.docpos);
            
operation(cm, function() {
              var 
end replaceRange(cmtext.join(""), pospos"paste");
              
setSelection(cmposend);
            })();
          }
        };
        
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.view.draggingText && !(posLess(poscm.view.sel.from) || posLess(cm.view.sel.topos))) {
        
cm.view.draggingText(e);
        if (
iesetTimeout(bind(focusInputcm), 50);
        return;
      }
      try {
        var 
text e.dataTransfer.getData("Text");
        if (
text) {
          var 
curFrom cm.view.sel.fromcurTo cm.view.sel.to;
          
setSelection(cmpospos);
          if (
cm.view.draggingTextreplaceRange(cm""curFromcurTo"paste");
          
cm.replaceSelection(textnull"paste");
          
focusInput(cm);
          
onFocus(cm);
        }
      }
      catch(
e){}
    }
  }

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

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

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

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

  function 
onDragStart(cme) {
    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)
      
e.dataTransfer.setDragImage(elt('img'), 00);
  }

  function 
setScrollTop(cmval) {
    if (
Math.abs(cm.view.scrollTop val) < 2) return;
    
cm.view.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, []);
  }
  function 
setScrollLeft(cmvalisScroller) {
    if (
isScroller val == cm.view.scrollLeft Math.abs(cm.view.scrollLeft val) < 2) return;
    
cm.view.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 0wheelDXwheelDYwheelStartXwheelStartYwheelPixelsPerUnit 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;

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

    var 
scroll cm.display.scroller;
    
// 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);
      
wheelStartX null// Abort measurement, if in progress
      
return;
    }

    if (
dy && wheelPixelsPerUnit != null) {
      var 
pixels dy wheelPixelsPerUnit;
      var 
top cm.view.scrollTopbot top cm.display.wrapper.clientHeight;
      if (
pixels 0top Math.max(0top pixels 50);
      else 
bot Math.min(cm.view.doc.heightbot pixels 50);
      
updateDisplay(cm, [], {toptopbottombot});
    }

    if (
wheelSamples 20) {
      if (
wheelStartX == null) {
        
wheelStartX scroll.scrollLeftwheelStartY scroll.scrollTop;
        
wheelDX dxwheelDY dy;
        
setTimeout(function() {
          if (
wheelStartX == null) return;
          var 
movedX scroll.scrollLeft wheelStartX;
          var 
movedY scroll.scrollTop wheelStartY;
          var 
sample = (movedY && wheelDY && movedY wheelDY) ||
            (
movedX && wheelDX && movedX wheelDX);
          
wheelStartX wheelStartY null;
          if (!
sample) return;
          
wheelPixelsPerUnit = (wheelPixelsPerUnit wheelSamples sample) / (wheelSamples 1);
          ++
wheelSamples;
        }, 
200);
      } else {
        
wheelDX += dxwheelDY += 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 
view cm.viewprevShift view.sel.shift;
    try {
      if (
isReadOnly(cm)) view.suppressEdits true;
      if (
dropShiftview.sel.shift false;
      
bound(cm);
    } catch(
e) {
      if (
!= Pass) throw e;
      return 
false;
    } finally {
      
view.sel.shift prevShift;
      
view.suppressEdits false;
    }
    return 
true;
  }

  function 
allKeyMaps(cm) {
    var 
maps cm.view.keyMaps.slice(0);
    
maps.push(cm.options.keyMap);
    if (
cm.options.extraKeysmaps.unshift(cm.options.extraKeys);
    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);
    }, 
50);

    var 
name keyNames[e_prop(e"keyCode")], handled false;
    var 
flipCtrlCmd mac && (opera || qtwebkit);
    if (
name == null || e.altGraphKey) return false;
    if (
e_prop(e"altKey")) name "Alt-" name;
    if (
e_prop(eflipCtrlCmd "metaKey" "ctrlKey")) name "Ctrl-" name;
    if (
e_prop(eflipCtrlCmd "ctrlKey" "metaKey")) name "Cmd-" name;

    var 
stopped false;
    function 
stop() { stopped true; }
    var 
keymaps allKeyMaps(cm);

    if (
e_prop(e"shiftKey")) {
      
handled lookupKey("Shift-" namekeymaps,
                          function(
b) {return doHandleBinding(cmbtrue);}, stop)
        || 
lookupKey(namekeymaps, function(b) {
          if (
typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cmb);
        }, 
stop);
    } else {
      
handled lookupKey(namekeymaps,
                          function(
b) { return doHandleBinding(cmb); }, stop);
    }
    if (
stoppedhandled false;
    if (
handled) {
      
e_preventDefault(e);
      
restartBlink(cm);
      if (
ie_lt9) { e.oldKeyCode e.keyCodee.keyCode 0; }
    }
    return 
handled;
  }

  function 
handleCharBinding(cmech) {
    var 
handled lookupKey("'" ch "'"allKeyMaps(cm),
                            function(
b) { return doHandleBinding(cmbtrue); });
    if (
handled) {
      
e_preventDefault(e);
      
restartBlink(cm);
    }
    return 
handled;
  }

  var 
lastStoppedKey null;
  function 
onKeyDown(e) {
    var 
cm this;
    if (!
cm.view.focusedonFocus(cm);
    if (
ie && e.keyCode == 27) { e.returnValue false; }
    if (
cm.options.onKeyEvent && cm.options.onKeyEvent(cmaddStop(e))) return;
    var 
code e_prop(e"keyCode");
    
// IE does strange things with escape.
    
cm.view.sel.shift code == 16 || e_prop(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 && e_prop(emac "metaKey" "ctrlKey"))
        
cm.replaceSelection("");
    }
  }

  function 
onKeyPress(e) {
    var 
cm this;
    if (
cm.options.onKeyEvent && cm.options.onKeyEvent(cmaddStop(e))) return;
    var 
keyCode e_prop(e"keyCode"), charCode e_prop(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.view.mode.electricChars &&
        
this.options.smartIndent && !isReadOnly(this) &&
        
this.view.mode.electricChars.indexOf(ch) > -1)
      
setTimeout(operation(cm, function() {indentLine(cmcm.view.sel.to.line"smart");}), 75);
    if (
handleCharBinding(cmech)) return;
    
fastPoll(cm);
  }

  function 
onFocus(cm) {
    if (
cm.options.readOnly == "nocursor") return;
    if (!
cm.view.focused) {
      
signal(cm"focus"cm);
      
cm.view.focused true;
      if (
cm.display.scroller.className.search(/bCodeMirror-focusedb/) == -1)
        
cm.display.scroller.className += " CodeMirror-focused";
      
resetInput(cmtrue);
    }
    
slowPoll(cm);
    
restartBlink(cm);
  }
  function 
onBlur(cm) {
    if (
cm.view.focused) {
      
signal(cm"blur"cm);
      
cm.view.focused false;
      
cm.display.scroller.className cm.display.scroller.className.replace(" CodeMirror-focused""");
    }
    
clearInterval(cm.display.blinker);
    
setTimeout(function() {if (!cm.view.focusedcm.view.sel.shift false;}, 150);
  }

  var 
detectingSelectAll;
  function 
onContextMenu(cme) {
    var 
display cm.displaysel cm.view.sel;
    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)(cmpospos);

    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; 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 
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) {
        
clearTimeout(detectingSelectAll);
        var 
extval display.input.value " " + (posEq(sel.fromsel.to) ? "" display.input.value), 0;
        
display.prevInput " ";
        
display.input.selectionStart 1display.input.selectionEnd extval.length;
        
detectingSelectAll setTimeout(function poll(){
          if (
display.prevInput == " " && display.input.selectionStart == 0)
            
operation(cmcommands.selectAll)(cm);
          else if (
i++ < 10detectingSelectAll setTimeout(poll500);
          else 
resetInput(cm);
        }, 
200);
      }
    }

    if (
gecko) {
      
e_stop(e);
      
on(window"mouseup", function mouseup() {
        
off(window"mouseup"mouseup);
        
setTimeout(rehide20);
      });
    } else {
      
setTimeout(rehide50);
    }
  }

  
// UPDATING

  // Replace the range from from to to by the strings in newText.
  // Afterwards, set the selection to selFrom, selTo.
  
function updateDoc(cmfromtonewTextselUpdateorigin) {
    
// Possibly split or suppress the update based on the presence
    // of read-only spans in its range.
    
var split sawReadOnlySpans &&
      
removeReadOnlyRanges(cm.view.docfromto);
    if (
split) {
      for (var 
split.length 1>= 1; --i)
        
updateDocInner(cmsplit[i].fromsplit[i].to, [""], origin);
      if (
split.length)
        return 
updateDocInner(cmsplit[0].fromsplit[0].tonewTextselUpdateorigin);
    } else {
      return 
updateDocInner(cmfromtonewTextselUpdateorigin);
    }
  }

  function 
updateDocInner(cmfromtonewTextselUpdateorigin) {
    if (
cm.view.suppressEdits) return;

    var 
view cm.viewdoc view.docold = [];
    
doc.iter(from.lineto.line 1, function(line) {
      
old.push(newHL(line.textline.markedSpans));
    });
    var 
startSelFrom view.sel.fromstartSelTo view.sel.to;
    var 
lines updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.chto.chnewText);
    var 
retval updateDocNoUndo(cmfromtolinesselUpdateorigin);
    if (
view.historyaddChange(cmfrom.linenewText.lengtholdorigin,
                                
startSelFromstartSelToview.sel.fromview.sel.to);
    return 
retval;
  }

  function 
unredoHelper(cmtype) {
    var 
doc cm.view.dochist cm.view.history;
    var 
set = (type == "undo" hist.done hist.undone).pop();
    if (!
set) return;
    var 
anti = {events: [], fromBeforeset.fromAftertoBeforeset.toAfter,
                
fromAfterset.fromBeforetoAfterset.toBefore};
    for (var 
set.events.length 1>= 0-= 1) {
      
hist.dirtyCounter += type == "undo" ? -1;
      var 
change set.events[i];
      var 
replaced = [], end change.start change.added;
      
doc.iter(change.startend, function(line) { replaced.push(newHL(line.textline.markedSpans)); });
      
anti.events.push({startchange.startaddedchange.old.lengtholdreplaced});
      var 
selPos null : {fromset.fromBeforetoset.toBefore};
      
updateDocNoUndo(cm, {linechange.startch0}, {lineend 1chgetLine(docend-1).text.length},
                      
change.oldselPostype);
    }
    (
type == "undo" hist.undone hist.done).push(anti);
  }

  function 
updateDocNoUndo(cmfromtolinesselUpdateorigin) {
    var 
view cm.viewdoc view.docdisplay cm.display;
    if (
view.suppressEdits) return;

    var 
nlines to.line from.linefirstLine getLine(docfrom.line), lastLine getLine(docto.line);
    var 
recomputeMaxLength falsecheckWidthStart from.line;
    if (!
cm.options.lineWrapping) {
      
checkWidthStart lineNo(visualLine(docfirstLine));
      
doc.iter(checkWidthStartto.line 1, function(line) {
        if (
lineLength(docline) == view.maxLineLength) {
          
recomputeMaxLength true;
          return 
true;
        }
      });
    }

    var 
lastHL lst(lines), th textHeight(display);

    
// First adjust the line structure
    
if (from.ch == && to.ch == && hlText(lastHL) == "") {
      
// This is a whole-line replace. Treated specially to make
      // sure line objects move the way they are supposed to.
      
var added = [];
      for (var 
0lines.length 1e; ++i)
        
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
      
updateLine(cmlastLinelastLine.texthlSpans(lastHL));
      if (
nlinesdoc.remove(from.linenlinescm);
      if (
added.lengthdoc.insert(from.lineadded);
    } else if (
firstLine == lastLine) {
      if (
lines.length == 1) {
        
updateLine(cmfirstLinefirstLine.text.slice(0from.ch) + hlText(lines[0]) +
                   
firstLine.text.slice(to.ch), hlSpans(lines[0]));
      } else {
        for (var 
added = [], 1lines.length 1e; ++i)
          
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
        
added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
        
updateLine(cmfirstLinefirstLine.text.slice(0from.ch) + hlText(lines[0]), hlSpans(lines[0]));
        
doc.insert(from.line 1added);
      }
    } else if (
lines.length == 1) {
      
updateLine(cmfirstLinefirstLine.text.slice(0from.ch) + hlText(lines[0]) +
                 
lastLine.text.slice(to.ch), hlSpans(lines[0]));
      
doc.remove(from.line 1nlinescm);
    } else {
      var 
added = [];
      
updateLine(cmfirstLinefirstLine.text.slice(0from.ch) + hlText(lines[0]), hlSpans(lines[0]));
      
updateLine(cmlastLinehlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
      for (var 
1lines.length 1e; ++i)
        
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
      if (
nlines 1doc.remove(from.line 1nlines 1cm);
      
doc.insert(from.line 1added);
    }

    if (
cm.options.lineWrapping) {
      var 
perLine Math.max(5display.scroller.clientWidth charWidth(display) - 3);
      
doc.iter(from.linefrom.line lines.length, function(line) {
        if (
line.height == 0) return;
        var 
guess = (Math.ceil(line.text.length perLine) || 1) * th;
        if (
guess != line.heightupdateLineHeight(lineguess);
      });
    } else {
      
doc.iter(checkWidthStartfrom.line lines.length, function(line) {
        var 
len lineLength(docline);
        if (
len view.maxLineLength) {
          
view.maxLine line;
          
view.maxLineLength len;
          
view.maxLineChanged true;
          
recomputeMaxLength false;
        }
      });
      if (
recomputeMaxLengthcm.curOp.updateMaxLine true;
    }

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

    var 
lendiff lines.length nlines 1;
    
// Remember that these lines changed, for updating the display
    
regChange(cmfrom.lineto.line 1lendiff);
    if (
hasHandler(cm"change")) {
      
// Normalize lines to contain only strings, since that's what
      // the change event handler expects
      
for (var 0lines.length; ++i)
        if (
typeof lines[i] != "string"lines[i] = lines[i].text;
      var 
changeObj = {fromfromtototextlinesoriginorigin};
      if (
cm.curOp.textChanged) {
        for (var 
cur cm.curOp.textChangedcur.nextcur cur.next) {}
        
cur.next changeObj;
      } else 
cm.curOp.textChanged changeObj;
    }

    
// Update the selection
    
var newSelFromnewSelToend = {linefrom.line lines.length 1,
                                     
chhlText(lastHL).length  + (lines.length == from.ch 0)};
    if (
selUpdate && typeof selUpdate != "string") {
      if (
selUpdate.from) { newSelFrom selUpdate.fromnewSelTo selUpdate.to; }
      else 
newSelFrom newSelTo selUpdate;
    } else if (
selUpdate == "end") {
      
newSelFrom newSelTo end;
    } else if (
selUpdate == "start") {
      
newSelFrom newSelTo from;
    } else if (
selUpdate == "around") {
      
newSelFrom fromnewSelTo end;
    } else {
      var 
adjustPos = function(pos) {
        if (
posLess(posfrom)) return pos;
        if (!
posLess(topos)) return end;
        var 
line pos.line lendiff;
        var 
ch pos.ch;
        if (
pos.line == to.line)
          
ch += hlText(lastHL).length - (to.ch - (to.line == from.line from.ch 0));
        return {
linelinechch};
      };
      
newSelFrom adjustPos(view.sel.from);
      
newSelTo adjustPos(view.sel.to);
    }
    
setSelection(cmnewSelFromnewSelTonulltrue);
    return 
end;
  }

  function 
replaceRange(cmcodefromtoorigin) {
    if (!
toto from;
    if (
posLess(tofrom)) { var tmp toto fromfrom tmp; }
    return 
updateDoc(cmfromtosplitLines(code), nullorigin);
  }

  
// SELECTION

  
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 {linex.linechx.ch};}

  function 
clipLine(docn) {return Math.max(0Math.min(ndoc.size-1));}
  function 
clipPos(docpos) {
    if (
pos.line 0) return {line0ch0};
    if (
pos.line >= doc.size) return {linedoc.size-1chgetLine(docdoc.size-1).text.length};
    var 
ch pos.chlinelen getLine(docpos.line).text.length;
    if (
ch == null || ch linelen) return {linepos.linechlinelen};
    else if (
ch 0) return {linepos.linech0};
    else return 
pos;
  }
  function 
isLine(docl) {return >= && doc.size;}

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

  
// 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(cmanchorheadbiascheckAtomic) {
    
cm.view.goalColumn null;
    var 
sel cm.view.sel;
    
// Skip over atomic spans.
    
if (checkAtomic || !posEq(anchorsel.anchor))
      
anchor skipAtomic(cmanchorbiascheckAtomic != "push");
    if (
checkAtomic || !posEq(headsel.head))
      
head skipAtomic(cmheadbiascheckAtomic != "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;

    
cm.curOp.updateInput true;
    
cm.curOp.selectionChanged true;
  }

  function 
reCheckSelection(cm) {
    
setSelection(cmcm.view.sel.fromcm.view.sel.tonull"push");
  }

  function 
skipAtomic(cmposbiasmayClear) {
    var 
doc cm.view.docflipped falsecurPos pos;
    var 
dir bias || 1;
    
cm.view.cantEdit false;
    
search: for (;;) {
      var 
line getLine(doccurPos.line), toClear;
      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 && m.clearOnEnter) {
              (
toClear || (toClear = [])).push(m);
              continue;
            } else if (!
m.atomic) continue;
            var 
newPos m.find()[dir "from" "to"];
            if (
posEq(newPoscurPos)) {
              
newPos.ch += dir;
              if (
newPos.ch 0) {
                if (
newPos.linenewPos clipPos(doc, {linenewPos.line 1});
                else 
newPos null;
              } else if (
newPos.ch line.text.length) {
                if (
newPos.line doc.size 1newPos = {linenewPos.line 1ch0};
                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(cmposbiastrue);
                  
// Otherwise, turn off editing until further notice, and return the start of the doc
                  
cm.view.cantEdit true;
                  return {
line0ch0};
                }
                
flipped truenewPos posdir = -dir;
              }
            }
            
curPos newPos;
            continue 
search;
          }
        }
        if (
toClear) for (var 0toClear.length; ++itoClear[i].clear();
      }
      return 
curPos;
    }
  }

  
// SCROLLING

  
function scrollCursorIntoView(cm) {
    var 
view cm.view;
    var 
coords scrollPosIntoView(cmview.sel.head);
    if (!
view.focused) return;
    var 
display cm.displaybox display.sizer.getBoundingClientRect(), doScroll null;
    if (
coords.top box.top 0doScroll true;
    else if (
coords.bottom box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll false;
    if (
doScroll != null && !phantom) {
      var 
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(cmpos) {
    for (;;) {
      var 
changed falsecoords cursorCoords(cmpos);
      var 
scrollPos calculateScrollPos(cmcoords.leftcoords.topcoords.leftcoords.bottom);
      var 
startTop cm.view.scrollTopstartLeft cm.view.scrollLeft;
      if (
scrollPos.scrollTop != null) {
        
setScrollTop(cmscrollPos.scrollTop);
        if (
Math.abs(cm.view.scrollTop startTop) > 1changed true;
      }
      if (
scrollPos.scrollLeft != null) {
        
setScrollLeft(cmscrollPos.scrollLeft);
        if (
Math.abs(cm.view.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.displaypt paddingTop(display);
    
y1 += pty2 += pt;
    var 
screen display.scroller.clientHeight scrollerCutOffscreentop display.scroller.scrollTopresult = {};
    var 
docBottom cm.view.doc.height pt;
    var 
atTop y1 pt 10atBottom y2 pt docBottom 10;
    if (
y1 screentopresult.scrollTop atTop Math.max(0y1);
    else if (
y2 screentop screenresult.scrollTop = (atBottom docBottom y2) - screen;

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

  
// API UTILITIES

  
function indentLine(cmnhowaggressive) {
    var 
doc cm.view.doc;
    if (!
howhow "add";
    if (
how == "smart") {
      if (!
cm.view.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.view.mode.indent(stateline.text.slice(curSpaceString.length), line.text);
      if (
indentation == Pass) {
        if (!
aggressive) return;
        
how "prev";
      }
    }
    if (
how == "prev") {
      if (
nindentation 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;
    
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(cmindentString, {linench0}, {linenchcurSpaceString.length}, "input");
    
line.stateAfter null;
  }

  function 
changeLine(cmhandleop) {
    var 
no handleline handledoc cm.view.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(cmdirunitvisually) {
    var 
doc cm.view.docend cm.view.sel.headline end.linech end.ch;
    var 
lineObj getLine(docline);
    function 
findNextLine() {
      var 
line dir;
      if (
|| == doc.size) return 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 
false;
      } else 
ch next;
      return 
true;
    }
    if (
unit == "char"moveOnce();
    else if (
unit == "column"moveOnce(true);
    else if (
unit == "word") {
      var 
sawWord false;
      for (;;) {
        if (
dir 0) if (!moveOnce()) break;
        if (
isWordChar(lineObj.text.charAt(ch))) sawWord true;
        else if (
sawWord) {if (dir 0) {dir 1moveOnce();} break;}
        if (
dir 0) if (!moveOnce()) break;
      }
    }
    return 
skipAtomic(cm, {linelinechch}, dirtrue);
  }

  function 
findWordAt(linepos) {
    var 
start pos.chend pos.ch;
    if (
line) {
      if (
pos.after === false || end == line.length) --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 {
from: {linepos.linechstart}, to: {linepos.linechend}};
  }

  function 
selectLine(cmline) {
    
extendSelection(cm, {linelinech0}, clipPos(cm.view.doc, {lineline 1ch0}));
  }

  
// PROTOTYPE

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

  
CodeMirror.prototype = {
    
getValue: function(lineSep) {
      var 
text = [], doc this.view.doc;
      
doc.iter(0doc.size, function(line) { text.push(line.text); });
      return 
text.join(lineSep || "n");
    },

    
setValueoperation(null, function(code) {
      var 
doc this.view.doctop = {line0ch0}, lastLen getLine(docdoc.size-1).text.length;
      
updateDocInner(thistop, {linedoc.size 1chlastLen}, splitLines(code), toptop"setValue");
    }),

    
getSelection: function(lineSep) { return this.getRange(this.view.sel.fromthis.view.sel.tolineSep); },

    
replaceSelectionoperation(null, function(codecollapseorigin) {
      var 
sel this.view.sel;
      
updateDoc(thissel.fromsel.tosplitLines(code), collapse || "around"origin);
    }),

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

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

    
addKeyMap: function(map) {
      
this.view.keyMaps.push(map);
    },

    
removeKeyMap: function(map) {
      var 
maps this.view.keyMaps;
      for (var 
0maps.length; ++i)
        if ((
typeof map == "string" maps[i].name maps[i]) == map) {
          
maps.splice(i1);
          return 
true;
        }
    },

    
undooperation(null, function() {unredoHelper(this"undo");}),
    
redooperation(null, function() {unredoHelper(this"redo");}),

    
indentLineoperation(null, function(ndiraggressive) {
      if (
typeof dir != "string") {
        if (
dir == nulldir this.options.smartIndent "smart" "prev";
        else 
dir dir "add" "subtract";
      }
      if (
isLine(this.view.docn)) indentLine(thisndiraggressive);
    }),

    
indentSelectionoperation(null, function(how) {
      var 
sel this.view.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);
    }),

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

    
clearHistory: function() {this.view.history makeHistory();},

    
markClean: function() {
      
this.view.history.dirtyCounter 0;
      
this.view.history.lastOp this.view.history.lastOrigin null;
    },

    
isClean: function () {return this.view.history.dirtyCounter == 0;},
      
    
getHistory: function() {
      var 
hist this.view.history;
      function 
cp(arr) {
        for (var 
0nw = [], nweltarr.length; ++i) {
          var 
set arr[i];
          
nw.push({eventsnwelt = [], fromBeforeset.fromBeforetoBeforeset.toBefore,
                   
fromAfterset.fromAftertoAfterset.toAfter});
          for (var 
0elt set.eventselt.length; ++j) {
            var 
old = [], cur elt[j];
            
nwelt.push({startcur.startaddedcur.addedoldold});
            for (var 
0cur.old.length; ++kold.push(hlText(cur.old[k]));
          }
        }
        return 
nw;
      }
      return {
donecp(hist.done), undonecp(hist.undone)};
    },

    
setHistory: function(histData) {
      var 
hist this.view.history makeHistory();
      
hist.done histData.done;
      
hist.undone histData.undone;
    },

    
// Fetch the parser token for a given character. Useful for hacks
    // that want to inspect the mode state (say, for completion).
    
getTokenAt: function(pos) {
      var 
doc this.view.doc;
      
pos clipPos(docpos);
      var 
state getStateBefore(thispos.line), mode this.view.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};
    },

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

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

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

    
coordsChar: function(coords) {
      var 
off this.display.lineSpace.getBoundingClientRect();
      return 
coordsChar(thiscoords.left off.leftcoords.top off.top);
    },

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

    
markTextoperation(null, function(fromtooptions) {
      return 
markText(thisclipPos(this.view.docfrom), clipPos(this.view.docto),
                      
options"range");
    }),

    
setBookmarkoperation(null, function(poswidget) {
      
pos clipPos(this.view.docpos);
      return 
markText(thisposposwidget ? {replacedWithwidget} : {}, "bookmark");
    }),

    
findMarksAt: function(pos) {
      var 
doc this.view.doc;
      
pos clipPos(docpos);
      var 
markers = [], spans getLine(docpos.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);
      }
      return 
markers;
    },

    
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 
0cm thisdoc cm.view.doc;
      
doc.iter(0doc.size, 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("\b" cls "\b").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 
upd cur.replace(new RegExp("^" cls "\b\s*|\s*\b" cls "\b"), "");
          if (
upd == cur) return false;
          
line[prop] = upd || null;
        }
        return 
true;
      });
    }),

    
addLineWidgetoperation(null, function(handlenodeoptions) {
      var 
widget options || {};
      
widget.node node;
      if (
widget.noHScrollthis.display.alignWidgets true;
      
changeLine(thishandle, function(line) {
        (
line.widgets || (line.widgets = [])).push(widget);
        
widget.line line;
        return 
true;
      });
      return 
widget;
    }),

    
removeLineWidgetoperation(null, function(widget) {
      var 
ws widget.line.widgetsno lineNo(widget.line);
      if (
no == null) return;
      for (var 
0ws.length; ++i) if (ws[i] == widgetws.splice(i--, 1);
      
regChange(thisnono 1);
    }),

    
lineInfo: function(line) {
      if (
typeof line == "number") {
        if (!
isLine(this.view.docline)) return null;
        var 
line;
        
line getLine(this.view.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.view.docpos));
      var 
top pos.topleft pos.left;
      
node.style.position "absolute";
      
display.sizer.appendChild(node);
      if (
vert == "over"top pos.top;
      else if (
vert == "near") {
        var 
vspace Math.max(display.wrapper.clientHeightthis.view.doc.height),
        
hspace Math.max(display.sizer.clientWidthdisplay.lineSpace.clientWidth);
        if (
pos.bottom node.offsetHeight vspace && pos.top node.offsetHeight)
          
top pos.top node.offsetHeight;
        if (
left node.offsetWidth hspace)
          
left hspace node.offsetWidth;
      }
      
node.style.top = (top paddingTop(display)) + "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);
    },

    
lineCount: function() {return this.view.doc.size;},

    
clipPos: function(pos) {return clipPos(this.view.docpos);},

    
getCursor: function(start) {
      var 
sel this.view.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.view.sel.fromthis.view.sel.to);},

    
setCursoroperation(null, function(linechextend) {
      var 
pos clipPos(this.view.doctypeof line == "number" ? {linelinechch || 0} : line);
      if (
extendextendSelection(thispos);
      else 
setSelection(thispospos);
    }),

    
setSelectionoperation(null, function(anchorhead) {
      var 
doc this.view.doc;
      
setSelection(thisclipPos(docanchor), clipPos(dochead || anchor));
    }),

    
extendSelectionoperation(null, function(fromto) {
      var 
doc this.view.doc;
      
extendSelection(thisclipPos(docfrom), to && clipPos(docto));
    }),

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

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

    
getLineHandle: function(line) {
      var 
doc this.view.doc;
      if (
isLine(docline)) return getLine(docline);
    },

    
getLineNumber: function(line) {return lineNo(line);},

    
setLineoperation(null, function(linetext) {
      if (
isLine(this.view.docline))
        
replaceRange(thistext, {linelinech0}, {linelinechgetLine(this.view.docline).text.length});
    }),

    
removeLineoperation(null, function(line) {
      if (
isLine(this.view.docline))
        
replaceRange(this"", {linelinech0}, clipPos(this.view.doc, {lineline+1ch0}));
    }),

    
replaceRangeoperation(null, function(codefromto) {
      var 
doc this.view.doc;
      
from clipPos(docfrom);
      
to to clipPos(docto) : from;
      return 
replaceRange(thiscodefromto);
    }),

    
getRange: function(fromtolineSep) {
      var 
doc this.view.doc;
      
from clipPos(docfrom); to clipPos(docto);
      var 
l1 from.linel2 to.line;
      if (
l1 == l2) return getLine(docl1).text.slice(from.chto.ch);
      var 
code = [getLine(docl1).text.slice(from.ch)];
      
doc.iter(l1 1l2, function(line) { code.push(line.text); });
      
code.push(getLine(docl2).text.slice(0to.ch));
      return 
code.join(lineSep || "n");
    },

    
triggerOnKeyDownoperation(nullonKeyDown),

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

    
// Stuff used by commands, probably not much use to outside code.
    
moveHoperation(null, function(dirunit) {
      var 
sel this.view.selpos dir sel.from sel.to;
      if (
sel.shift || sel.extend || posEq(sel.fromsel.to)) pos findPosH(thisdirunittrue);
      
extendSelection(thisposposdir);
    }),

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

    
moveVoperation(null, function(dirunit) {
      var 
view this.viewdoc view.docdisplay this.display;
      var 
cur view.sel.headpos cursorCoords(thiscur"div");
      var 
pos.lefty;
      if (
view.goalColumn != nullview.goalColumn;
      if (
unit == "page") {
        var 
pageSize Math.min(display.wrapper.clientHeightwindow.innerHeight || document.documentElement.clientHeight);
        
pos.top dir pageSize;
      } else if (
unit == "line") {
        
dir pos.bottom pos.top 3;
      }
      do {
        var 
target coordsChar(thisxy);
        
+= dir 5;
      } while (
target.outside && (dir doc.height));

      if (
unit == "page"display.scrollbarV.scrollTop += charCoords(thistarget"div").top pos.top;
      
extendSelection(thistargettargetdir);
      
view.goalColumn x;
    }),

    
toggleOverwrite: function() {
      if (
this.view.overwrite = !this.view.overwrite)
        
this.display.cursor.className += " CodeMirror-overwrite";
      else
        
this.display.cursor.className this.display.cursor.className.replace(" CodeMirror-overwrite""");
    },

    
posFromIndex: function(off) {
      var 
lineNo 0chdoc this.view.doc;
      
doc.iter(0doc.size, function(line) {
        var 
sz line.text.length 1;
        if (
sz off) { ch off; return true; }
        
off -= sz;
        ++
lineNo;
      });
      return 
clipPos(doc, {linelineNochch});
    },
    
indexFromPos: function (coords) {
      if (
coords.line || coords.ch 0) return 0;
      var 
index coords.ch;
      
this.view.doc.iter(0coords.line, function (line) {
        
index += line.text.length 1;
      });
      return 
index;
    },

    
scrollTo: function(xy) {
      if (
!= nullthis.display.scrollbarH.scrollLeft this.display.scroller.scrollLeft x;
      if (
!= nullthis.display.scrollbarV.scrollTop this.display.scroller.scrollTop y;
      
updateDisplay(this, []);
    },
    
getScrollInfo: function() {
      var 
scroller this.display.scrollerco scrollerCutOff;
      return {
leftscroller.scrollLefttopscroller.scrollTop,
              
heightscroller.scrollHeight cowidthscroller.scrollWidth co,
              
clientHeightscroller.clientHeight coclientWidthscroller.clientWidth co};
    },

    
scrollIntoView: function(pos) {
      if (
typeof pos == "number"pos = {lineposch0};
      
pos pos clipPos(this.view.docpos) : this.view.sel.head;
      
scrollPosIntoView(thispos);
    },

    
setSize: 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);
      
this.refresh();
    },

    
on: function(typef) {on(thistypef);},
    
off: function(typef) {off(thistypef);},

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

    
refresh: function() {
      
clearCaches(this);
      if (
this.display.scroller.scrollHeight this.view.scrollTop)
        
this.display.scrollbarV.scrollTop this.display.scroller.scrollTop this.view.scrollTop;
      
updateDisplay(thistrue);
    },

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

  
// 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"nullloadModetrue);

  
option("indentUnit"2loadModetrue);
  
option("indentWithTabs"false);
  
option("smartIndent"true);
  
option("tabSize"4, function(cm) {
    
loadMode(cm);
    
clearCaches(cm);
    
updateDisplay(cmtrue);
  }, 
true);
  
option("electricChars"true);

  
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("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("cursorHeight"1);
  
option("workTime"100);
  
option("workDelay"100);
  
option("flattenSpans"true);
  
option("pollInterval"100);
  
option("undoDepth"40);
  
option("viewportMargin"10, function(cm){cm.refresh();}, true);

  
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 (
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] = {});
    for (var 
prop in properties) if (properties.hasOwnProperty(prop))
      
exts[prop] = properties[prop];
  };

  
// EXTENSIONS

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

  
CodeMirror.defineOption option;

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

  
// 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);
      
state info.state;
      
mode info.mode;
    }
    return 
info || {modemodestatestate};
  };

  
// STANDARD COMMANDS

  
var commands CodeMirror.commands = {
    
selectAll: function(cm) {cm.setSelection({line0ch0}, {linecm.lineCount() - 1});},
    
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(""from, {linefrom.line 1ch0}, "delete");
      else 
cm.replaceRange(""fromsel to : {linefrom.line}, "delete");
    },
    
deleteLine: function(cm) {
      var 
cm.getCursor().line;
      
cm.replaceRange("", {linelch0}, {linel}, "delete");
    },
    
undo: function(cm) {cm.undo();},
    
redo: function(cm) {cm.redo();},
    
goDocStart: function(cm) {cm.extendSelection({line0ch0});},
    
goDocEnd: function(cm) {cm.extendSelection({linecm.lineCount() - 1});},
    
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({linestart.linechinWS firstNonWS});
      } else 
cm.extendSelection(start);
    },
    
goLineEnd: function(cm) {
      
cm.extendSelection(lineEnd(cmcm.getCursor().line));
    },
    
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");},
    
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");},
    
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),
                        {
linecur.linechcur.ch 1}, {linecur.linechcur.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""goWordLeft""Ctrl-Right""goWordRight""Alt-Left""goLineStart""Alt-Right""goLineEnd",
    
"Ctrl-Backspace""delWordBefore""Ctrl-Delete""delWordAfter""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""goWordLeft",
    
"Alt-Right""goWordRight""Cmd-Left""goLineStart""Cmd-Right""goLineEnd""Alt-Backspace""delWordBefore",
    
"Ctrl-Alt-Backspace""delWordAfter""Alt-Delete""delWordAfter""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",
    
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(namemapshandlestop) {
    function 
lookup(map) {
      
map getKeyMap(map);
      var 
found map[name];
      if (
found === false) {
        if (
stopstop();
        return 
true;
      }
      if (
found != null && handle(found)) return true;
      if (
map.nofallthrough) {
        if (
stopstop();
        return 
true;
      }
      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) {
        if (
lookup(fallthrough[i])) return true;
      }
      return 
false;
    }

    for (var 
0maps.length; ++i)
      if (
lookup(maps[i])) return true;
  }
  function 
isModifierKey(event) {
    var 
name keyNames[e_prop(event"keyCode")];
    return 
name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
  }
  
CodeMirror.isModifierKey isModifierKey;

  
// FROMTEXTAREA

  
CodeMirror.fromTextArea = function(textareaoptions) {
    if (!
optionsoptions = {};
    
options.value textarea.value;
    if (!
options.tabindex && textarea.tabindex)
      
options.tabindex textarea.tabindex;
    
// 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) {
      
// Deplorable hack to make the submit method do the right thing.
      
on(textarea.form"submit"save);
      var 
form textarea.formrealSubmit form.submit;
      try {
        
form.submit = function wrappedSubmit() {
          
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;
  }

  
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() {return countColumn(this.stringthis.startthis.tabSize);},
    
indentation: function() {return countColumn(this.stringnullthis.tabSize);},
    
match: function(patternconsumecaseInsensitive) {
      if (
typeof pattern == "string") {
        var 
cased = function(str) {return caseInsensitive str.toLowerCase() : str;};
        if (
cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
          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(cmtype) {
    
this.lines = [];
    
this.type type;
    
this.cm cm;
  }

  
TextMarker.prototype.clear = function() {
    if (
this.explicitlyCleared) return;
    
startOperation(this.cm);
    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(line))
        
updateLineHeight(linetextHeight(this.cm.display));
    }
    if (
min != nullregChange(this.cmminmax 1);
    
this.lines.length 0;
    
this.explicitlyCleared true;
    if (
this.collapsed && this.cm.view.cantEdit) {
      
this.cm.view.cantEdit false;
      
reCheckSelection(this.cm);
    }
    
endOperation(this.cm);
    
signalLater(this.cmthis"clear");
  };

  
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 = {linefoundchspan.from};
        if (
span.to != nullto = {linefoundchspan.to};
      }
    }
    if (
this.type == "bookmark") return from;
    return 
from && {fromfromtoto};
  };

  function 
markText(cmfromtooptionstype) {
    var 
doc cm.view.doc;
    var 
marker = new TextMarker(cmtype);
    if (
type == "range" && !posLess(fromto)) return marker;
    if (
options) for (var opt in options) if (options.hasOwnProperty(opt))
      
marker[opt] = options[opt];
    if (
marker.replacedWith) {
      
marker.collapsed true;
      
marker.replacedWith elt("span", [marker.replacedWith], "CodeMirror-widget");
    }
    if (
marker.collapsedsawCollapsedSpans true;

    var 
curLine from.linesize 0collapsedAtStartcollapsedAtEnd;
    
doc.iter(curLineto.line 1, function(line) {
      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);
      if (
marker.collapsed && curLine == from.line && lineIsHidden(line))
        
updateLineHeight(line0);
      ++
curLine;
    });

    if (
marker.readOnly) {
      
sawReadOnlySpans true;
      if (
cm.view.history.done.length || cm.view.history.undone.length)
        
cm.clearHistory();
    }
    if (
marker.collapsed) {
      if (
collapsedAtStart != collapsedAtEnd)
        throw new 
Error("Inserting collapsed marker overlapping an existing one");
      
marker.size size;
      
marker.atomic true;
    }
    if (
marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
      
regChange(cmfrom.lineto.line 1);
    if (
marker.atomicreCheckSelection(cm);
    return 
marker;
  }

  
// 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.lines.push(line);
  }

  function 
markedSpansBefore(oldstartCh) {
    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) {
        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(oldstartChendCh) {
    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 && span.from != startCh) {
        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 
updateMarkedSpans(oldFirstoldLaststartChendChnewText) {
    if (!
oldFirst && !oldLast) return newText;
    
// Get the spans that 'stick out' on both sides
    
var first markedSpansBefore(oldFirststartCh);
    var 
last markedSpansAfter(oldLaststartChendCh);

    
// Next, merge those two ends
    
var sameLine newText.length == 1offset lst(newText).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);
        }
      }
    }

    var 
newMarkers = [newHL(newText[0], first)];
    if (!
sameLine) {
      
// Fill gap with whole-line-spans
      
var gap newText.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(newHL(newText[i+1], gapMarkers));
      
newMarkers.push(newHL(lst(newText), last));
    }
    return 
newMarkers;
  }

  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 
markers[i].find();
      for (var 
0parts.length; ++j) {
        var 
parts[j];
        if (!
posLess(m.fromp.to) || posLess(m.top.from)) continue;
        var 
newParts = [j1];
        if (
posLess(p.fromm.from)) newParts.push({fromp.fromtom.from});
        if (
posLess(m.top.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(line) {
    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.from == && sp.marker.inclusiveLeft && lineIsHiddenInner(linesp))
        return 
true;
    }
  }
  
window.lineIsHidden lineIsHidden;
  function 
lineIsHiddenInner(linespan) {
    if (
span.to == null || 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.from == span.to &&
          (
sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
          
lineIsHiddenInner(linesp)) return true;
    }
  }

  
// hl stands for history-line, a data structure that can be either a
  // string (line without markers) or a {text, markedSpans} object.
  
function hlText(val) { return typeof val == "string" val val.text; }
  function 
hlSpans(val) {
    if (
typeof val == "string") return null;
    var 
spans val.markedSpansout null;
    for (var 
0spans.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 
newHL(textspans) { return spans ? {texttextmarkedSpansspans} : text; }

  function 
detachMarkedSpans(line) {
    var 
spans line.markedSpans;
    if (!
spans) return;
    for (var 
0spans.length; ++i) {
      var 
lines spans[i].marker.lines;
      var 
ix indexOf(linesline);
      
lines.splice(ix1);
    }
    
line.markedSpans null;
  }

  function 
attachMarkedSpans(linespans) {
    if (!
spans) return;
    for (var 
0spans.length; ++i)
      
spans[i].marker.lines.push(line);
    
line.markedSpans spans;
  }

  
// LINE DATA STRUCTURE

  // Line objects. These hold state related to a line, including
  // highlighting info (the styles array).
  
function makeLine(textmarkedSpansheight) {
    var 
line = {texttextheightheight};
    
attachMarkedSpans(linemarkedSpans);
    if (
lineIsHidden(line)) line.height 0;
    return 
line;
  }

  function 
updateLine(cmlinetextmarkedSpans) {
    
line.text text;
    
line.stateAfter line.styles null;
    if (
line.order != nullline.order null;
    
detachMarkedSpans(line);
    
attachMarkedSpans(linemarkedSpans);
    if (
lineIsHidden(line)) line.height 0;
    else if (!
line.heightline.height textHeight(cm.display);
    
signalLater(cmline"change");
  }

  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 highlightLine(cmlinestate) {
    var 
mode cm.view.modeflattenSpans cm.options.flattenSpans;
    var 
changed = !line.stylespos 0curText ""curStyle null;
    var 
stream = new StringStream(line.textcm.options.tabSize), st line.styles || (line.styles = []);
    if (
line.text == "" && mode.blankLinemode.blankLine(state);
    while (!
stream.eol()) {
      var 
style mode.token(streamstate), substr stream.current();
      
stream.start stream.pos;
      if (!
flattenSpans || curStyle != style) {
        if (
curText) {
          
changed changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
          
st[pos++] = curTextst[pos++] = curStyle;
        }
        
curText substrcurStyle style;
      } else 
curText curText substr;
      
// Give up when line is ridiculously long
      
if (stream.pos 5000) break;
    }
    if (
curText) {
      
changed changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
      
st[pos++] = curTextst[pos++] = curStyle;
    }
    if (
stream.pos 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; }
    if (
pos != st.length) { st.length poschanged true; }
    return 
changed;
  }

  
// Lightweight form of highlight -- proceed over this line and
  // update state, but don't save a style array.
  
function processLine(cmlinestate) {
    var 
mode cm.view.mode;
    var 
stream = new StringStream(line.textcm.options.tabSize);
    if (
line.text == "" && mode.blankLinemode.blankLine(state);
    while (!
stream.eol() && stream.pos <= 5000) {
      
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(cmrealLinemeasure) {
    var 
mergedline realLinelineBeforesawBeforesimple true;
    while (
merged collapsedSpanAtStart(line)) {
      
simple false;
      
line getLine(cm.view.docmerged.find().from.line);
      if (!
lineBeforelineBefore line;
    }

    var 
builder = {preelt("pre"), col0pos0display: !measure,
                   
measurenulladdedOnefalsecmcm};
    if (
line.textClassbuilder.pre.className line.textClass;

    do {
      if (!
line.styles)
        
highlightLine(cmlineline.stateAfter getStateBefore(cmlineNo(line)));
      
builder.measure line == realLine && measure;
      
builder.pos 0;
      
builder.addToken builder.measure buildTokenMeasure buildToken;
      if (
measure && sawBefore && line != realLine && !builder.addedOne) {
        
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
        
builder.addedOne true;
      }
      var 
next insertLineContent(linebuilder);
      
sawBefore line == lineBefore;
      if (
next) {
        
line getLine(cm.view.docnext.to.line);
        
simple false;
      }
    } while (
next);

    if (
measure && !builder.addedOne)
      
measure[0] = builder.pre.appendChild(simple elt("span""u00a0") : zeroWidthElement(cm.display.measure));
    if (!
builder.pre.firstChild && !lineIsHidden(realLine))
      
builder.pre.appendChild(document.createTextNode("u00a0"));

    return 
builder.pre;
  }

  var 
tokenSpecialChars = /[tu0000-u0019u200bu2028u2029uFEFF]/g;
  function 
buildToken(buildertextstylestartStyleendStyle) {
    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;
      return 
builder.pre.appendChild(elt("span", [content], fullStyle));
    }
    
builder.pre.appendChild(content);
  }

  function 
buildTokenMeasure(buildertextstylestartStyleendStyle) {
    for (var 
0text.length; ++i) {
      if (
&& text.length &&
          
builder.cm.options.lineWrapping &&
          
spanAffectsWrapping.test(text.slice(11)))
        
builder.pre.appendChild(elt("wbr"));
      
builder.measure[builder.pos++] =
        
buildToken(buildertext.charAt(i), style,
                   
== && startStyle== text.length && endStyle);
    }
    if (
text.lengthbuilder.addedOne true;
  }

  function 
buildCollapsedSpan(buildersizewidget) {
    if (
widget) {
      if (!
builder.displaywidget widget.cloneNode(true);
      
builder.pre.appendChild(widget);
      if (
builder.measure && size) {
        
builder.measure[builder.pos] = widget;
        
builder.addedOne true;
      }
    }
    
builder.pos += size;
  }

  
// Outputs a number of spans to make up a line, taking highlighting
  // and marked text into account.
  
function insertLineContent(linebuilder) {
    var 
st line.stylesspans line.markedSpans;
    if (!
spans) {
      for (var 
0st.lengthi+=2)
        
builder.addToken(builderst[i], styleToClass(st[i+1]));
      return;
    }

    var 
allText line.textlen allText.length;
    var 
pos 00text ""style;
    var 
nextChange 0spanStylespanEndStylespanStartStylecollapsed;
    for (;;) {
      if (
nextChange == pos) { // Update current marker set
        
spanStyle spanEndStyle spanStartStyle "";
        
collapsed nullnextChange Infinity;
        var 
foundBookmark null;
        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.collapsed && (!collapsed || collapsed.marker.width m.width))
              
collapsed sp;
          } else if (
sp.from pos && nextChange sp.from) {
            
nextChange sp.from;
          }
          if (
m.type == "bookmark" && sp.from == pos && m.replacedWith)
            
foundBookmark m.replacedWith;
        }
        if (
collapsed && (collapsed.from || 0) == pos) {
          
buildCollapsedSpan(builder, (collapsed.to == null len collapsed.to) - pos,
                             
collapsed.from != null && collapsed.marker.replacedWith);
          if (
collapsed.to == null) return collapsed.marker.find();
        }
        if (
foundBookmark && !collapsedbuildCollapsedSpan(builder0foundBookmark);
      }
      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 spanStyle,
                             
spanStartStylepos tokenText.length == nextChange spanEndStyle "");
          }
          if (
end >= upto) {text text.slice(upto pos); pos upto; break;}
          
pos end;
          
spanStartStyle "";
        }
        
text st[i++]; style styleToClass(st[i++]);
      }
    }
  }

  
// DOCUMENT DATA STRUCTURE

  
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; },
    
remove: function(atncm) {
      for (var 
atat ne; ++i) {
        var 
line this.lines[i];
        
this.height -= line.height;
        
cleanUpLine(line);
        
signalLater(cmline"delete");
      }
      
this.lines.splice(atn);
    },
    
collapse: function(lines) {
      
lines.splice.apply(lines, [lines.length0].concat(this.lines));
    },
    
insertHeight: 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; },
    
remove: function(atncallbacks) {
      
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.remove(atrmcallbacks);
          
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);
    },
    
insert: function(atlines) {
      var 
height 0;
      for (var 
0lines.lengthe; ++iheight += lines[i].height;
      
this.insertHeight(atlinesheight);
    },
    
insertHeight: 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.insertHeight(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();
    },
    
iter: function(fromtoop) { this.iterN(fromto fromop); },
    
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;
      }
    }
  };

  
// LINE UTILITIES

  
function getLine(chunkn) {
    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 
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;
  }

  function 
lineAtHeight(chunkh) {
    var 
0;
    
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.view.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() {
    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: [],
      
// Used to track when changes can be merged into a single undo
      // event
      
lastTime0lastOpnulllastOriginnull,
      
// Used by the isClean() method
      
dirtyCounter0
    
};
  }

  function 
addChange(cmstartaddedoldoriginfromBeforetoBeforefromAftertoAfter) {
    var 
history cm.view.history;
    
history.undone.length 0;
    var 
time = +new Datecur lst(history.done);
    
    if (
cur &&
        (
history.lastOp == cm.curOp.id ||
         
history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
         
history.lastTime time 600)) {
      
// Merge this change into the last event
      
var last lst(cur.events);
      if (
last.start start old.length || last.start last.added start) {
        
// Doesn't intersect with last sub-event, add new sub-event
        
cur.events.push({startstartaddedaddedoldold});
      } else {
        
// Patch up the last sub-event
        
var startBefore Math.max(0last.start start),
        
endAfter Math.max(0, (start old.length) - (last.start last.added));
        for (var 
startBefore0; --ilast.old.unshift(old[1]);
        for (var 
endAfter0; --ilast.old.push(old[old.length i]);
        if (
startBeforelast.start start;
        
last.added += added - (old.length startBefore endAfter);
      }
      
cur.fromAfter fromAftercur.toAfter toAfter;
    } else {
      
// Can not be merged, start a new event.
      
cur = {events: [{startstartaddedaddedoldold}],
             
fromBeforefromBeforetoBeforetoBeforefromAfterfromAftertoAftertoAfter};
      
history.done.push(cur);
      while (
history.done.length cm.options.undoDepth)
        
history.done.shift();
      if (
history.dirtyCounter 0)
          
// The user has made a change after undoing past the last clean state. 
          // We can never get back to a clean state now until markClean() is called.
          
history.dirtyCounter NaN;
      else
        
history.dirtyCounter++;
    }
    
history.lastTime time;
    
history.lastOp cm.curOp.id;
    
history.lastOrigin origin;
  }

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

  
// Allow 3rd-party code to override event properties by adding an override
  // object to an event object.
  
function e_prop(eprop) {
    var 
overridden e.override && e.override.hasOwnProperty(prop);
    return 
overridden e.override[prop] : e[prop];
  }

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

  function 
signalLater(cmemittertype /*, values...*/) {
    var 
arr emitter._handlers && emitter._handlers[type];
    if (!
arr) return;
    var 
args = Array.prototype.slice.call(arguments3), flist cm.curOp && cm.curOp.delayedCallbacks;
    function 
bnd(f) {return function(){f.apply(nullargs);};};
    for (var 
0arr.length; ++i)
      if (
flistflist.push(bnd(arr[i]));
      else 
arr[i].apply(nullargs);
  }

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

  
CodeMirror.on onCodeMirror.off offCodeMirror.signal signal;

  
// 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(stringendtabSize) {
    if (
end == null) {
      
end string.search(/[^su00a0]/);
      if (
end == -1end string.length;
    }
    for (var 
00end; ++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 
node.select();
  }

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

  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-u9fcc]/;
  function 
isWordChar(ch) {
    return /
w/.test(ch) || ch "x80" &&
      (
ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
  }

  function 
isEmpty(obj) {
    var 
0;
    for (var 
n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
    return !
c;
  }

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

  
// 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) {
    
e.innerHTML "";
    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;
  }

  
// 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 regexps matches the
  // character combinations that suffer from this phenomenon on the
  // various browsers.
  
var spanAffectsWrapping = /^$/; // Won't match any two-character string
  
if (geckospanAffectsWrapping = /$'/;
  else if (safari) spanAffectsWrapping = /-[^ -?]|?[^ !'"),.-/:;?]}]/;
  else if (chrome) spanAffectsWrapping = /-[^ -.?]|?[^ -.?]}:;!'"
),/]|[.!"#&%)*+,:;=>]|}~][({[<]|$'/;

  var knownScrollbarWidth;
  function scrollbarWidth(measure) {
    if (knownScrollbarWidth != null) return knownScrollbarWidth;
    var test = elt("
div", null, null, "width50pxheight50pxoverflow-xscroll");
    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, "displayinline-blockwidth1pxmargin-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");
    for (var i = 0; i < order.length; ++i) {
      var part = order[i];
      if (part.from < to && part.to > from)
        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "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.view.doc, lineN);
    var visual = visualLine(cm.view.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 {line: lineN, ch: ch};
  }
  function lineEnd(cm, lineNo) {
    var merged, line;
    while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
      lineNo = merged.find().to.line;
    var order = getOrder(line);
    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
    return {line: lineNo, ch: ch};
  }

  // 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 moveOneUnit = byUnit ? function(pos, dir) {
      do pos += dir;
      while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
      return pos;
    } : function(pos, dir) { return pos + dir; };
    var linedir = bidi[0].level;
    for (var i = 0; i < bidi.length; ++i) {
      var part = bidi[i], sticky = part.level % 2 == linedir;
      if ((part.from < start && part.to > start) ||
          (sticky && (part.from == start || part.to == start))) break;
    }
    var target = moveOneUnit(start, part.level % 2 ? -dir : dir);

    while (target != null) {
      if (part.level % 2 == linedir) {
        if (target < part.from || target > part.to) {
          part = bidi[i += dir];
          target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
        } else break;
      } else {
        if (target == bidiLeft(part)) {
          part = bidi[--i];
          target = part && bidiRight(part);
        } else if (target == bidiRight(part)) {
          part = bidi[++i];
          target = part && bidiLeft(part);
        } else break;
      }
    }

    return target < 0 || target > line.text.length ? null : target;
  }

  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]/;

    return function charOrdering(str) {
      if (!bidiRE.test(str)) return false;
      var len = str.length, types = [], startType = null;
      for (var i = 0, type; i < len; ++i) {
        types.push(type = charType(str.charCodeAt(i)));
        if (startType == null) {
          if (type == "L") startType = "L";
          else if (type == "R" || type == "r") startType = "R";
        }
      }
      if (startType == null) startType = "L";

      // 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 = startType; 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 = startType; 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 = startType; 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] : startType) == "L";
          var after = (end < len - 1 ? types[end] : startType) == "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.0";

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