Changeset - a30689fc4f61
[Not reviewed]
beta
0 1 0
Marcin Kuzminski - 14 years ago 2011-10-27 19:05:37
marcin@python-works.com
bumped codemirror to latest version
1 file changed with 732 insertions and 315 deletions:
0 comments (0 inline, 0 general)
rhodecode/public/js/codemirror.js
Show inline comments
 
@@ -13,32 +13,45 @@ var CodeMirror = (function() {
 
      if (defaults.hasOwnProperty(opt))
 
        options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
 

	
 
    // The element in which the editor lives. Takes care of scrolling
 
    // (if enabled).
 
    var wrapper = document.createElement("div");
 
    var targetDocument = options["document"];
 
    // The element in which the editor lives.
 
    var wrapper = targetDocument.createElement("div");
 
    wrapper.className = "CodeMirror";
 
    // This mess creates the base DOM structure for the editor.
 
    wrapper.innerHTML =
 
      '<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
 
        '<textarea style="position: absolute; width: 10000px;" wrap="off" ' +
 
          'autocorrect="off" autocapitalize="off"></textarea></div>' +
 
      '<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
 
      '<div style="position: relative">' + // Set to the height of the text, causes scrolling
 
        '<pre style="position: relative; height: 0; visibility: hidden; overflow: hidden;">' + // To measure line/char size
 
           '<span>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</span></pre>' +
 
          '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
 
        '<div style="position: relative">' + // Moved around its parent to cover visible view
 
          '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
 
          '<div style="overflow: hidden; position: absolute; width: 0; left: 0">' + // Wraps and hides input textarea
 
            '<textarea style="height: 1px; position: absolute; width: 1px;" wrap="off"></textarea></div>' +
 
          // Provides positioning relative to (visible) text origin
 
          '<div class="CodeMirror-lines"><div style="position: relative">' +
 
            '<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
 
            '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
 
            '<div></div></div></div></div></div>'; // This DIV contains the actual code
 
              '<div></div>' + // This DIV contains the actual code
 
            '</div></div></div></div></div>';
 
    if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
 
    // I've never seen more elegant code in my life.
 
    var code = wrapper.firstChild, measure = code.firstChild, mover = measure.nextSibling,
 
    var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
 
        scroller = wrapper.lastChild, code = scroller.firstChild,
 
        measure = code.firstChild, mover = measure.nextSibling,
 
        gutter = mover.firstChild, gutterText = gutter.firstChild,
 
        inputDiv = gutter.nextSibling, input = inputDiv.firstChild,
 
        lineSpace = inputDiv.nextSibling.firstChild, cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
 
        lineSpace = gutter.nextSibling.firstChild,
 
        cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
 
    if (options.tabindex != null) input.tabindex = options.tabindex;
 
    if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
 

	
 
    // Check for problem with IE innerHTML not working when we have a
 
    // P (or similar) parent node.
 
    try { stringWidth("x"); }
 
    catch (e) {
 
      if (e.message.match(/unknown runtime/i))
 
        e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
 
      throw e;
 
    }
 

	
 
    // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
 
    var poll = new Delayed(), highlight = new Delayed(), blinker;
 

	
 
@@ -46,7 +59,7 @@ var CodeMirror = (function() {
 
    // (see Line constructor), work an array of lines that should be
 
    // parsed, and history the undo history (instance of History
 
    // constructor).
 
    var mode, lines = [new Line("")], work, history = new History(), focused;
 
    var mode, lines = [new Line("")], work, focused;
 
    loadMode();
 
    // The selection. These are always maintained to point at valid
 
    // positions. Inverted is used to remember that the user is
 
@@ -56,10 +69,10 @@ var CodeMirror = (function() {
 
    // whether the user is holding shift. reducedSelection is a hack
 
    // to get around the fact that we can't create inverted
 
    // selections. See below.
 
    var shiftSelecting, reducedSelection;
 
    var shiftSelecting, reducedSelection, lastClick, lastDoubleClick, draggingText;
 
    // Variables used by startOperation/endOperation to track what
 
    // happened during the operation.
 
    var updateInput, changes, textChanged, selectionChanged, leaveInputAlone;
 
    var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
 
    // Current visible range (may be bigger than the view window).
 
    var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
 
    // editing will hold an object describing the things we put in the
 
@@ -67,35 +80,46 @@ var CodeMirror = (function() {
 
    // bracketHighlighted is used to remember that a backet has been
 
    // marked.
 
    var editing, bracketHighlighted;
 
    // Tracks the maximum line length so that the horizontal scrollbar
 
    // can be kept static when scrolling.
 
    var maxLine = "", maxWidth;
 

	
 
    // Initialize the content. Somewhat hacky (delayed prepareInput)
 
    // to work around browser issues.
 
    // Initialize the content.
 
    operation(function(){setValue(options.value || ""); updateInput = false;})();
 
    setTimeout(prepareInput, 20);
 
    var history = new History();
 

	
 
    // Register our event handlers.
 
    connect(wrapper, "mousedown", operation(onMouseDown));
 
    connect(scroller, "mousedown", operation(onMouseDown));
 
    connect(scroller, "dblclick", operation(onDoubleClick));
 
    connect(lineSpace, "dragstart", onDragStart);
 
    // 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 (!gecko) connect(wrapper, "contextmenu", operation(onContextMenu));
 
    connect(code, "dblclick", operation(onDblClick));
 
    connect(wrapper, "scroll", function() {updateDisplay([]); if (options.onScroll) options.onScroll(instance);});
 
    if (!gecko) connect(scroller, "contextmenu", onContextMenu);
 
    connect(scroller, "scroll", function() {
 
      updateDisplay([]);
 
      if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
 
      if (options.onScroll) options.onScroll(instance);
 
    });
 
    connect(window, "resize", function() {updateDisplay(true);});
 
    connect(input, "keyup", operation(onKeyUp));
 
    connect(input, "input", function() {fastPoll(curKeyId);});
 
    connect(input, "keydown", operation(onKeyDown));
 
    connect(input, "keypress", operation(onKeyPress));
 
    connect(input, "focus", onFocus);
 
    connect(input, "blur", onBlur);
 

	
 
    connect(wrapper, "dragenter", function(e){e.stop();});
 
    connect(wrapper, "dragover", function(e){e.stop();});
 
    connect(wrapper, "drop", operation(onDrop));
 
    connect(wrapper, "paste", function(){input.focus(); fastPoll();});
 
    connect(scroller, "dragenter", e_stop);
 
    connect(scroller, "dragover", e_stop);
 
    connect(scroller, "drop", operation(onDrop));
 
    connect(scroller, "paste", function(){focusInput(); fastPoll();});
 
    connect(input, "paste", function(){fastPoll();});
 
    connect(input, "cut", function(){fastPoll();});
 

	
 
    if (document.activeElement == input) onFocus();
 
    // IE throws unspecified error in certain cases, when
 
    // trying to access activeElement before onload
 
    var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
 
    if (hasFocus) setTimeout(onFocus, 20);
 
    else onBlur();
 

	
 
    function isLine(l) {return l >= 0 && l < lines.length;}
 
@@ -104,27 +128,37 @@ var CodeMirror = (function() {
 
    // range checking and/or clipping. operation is used to wrap the
 
    // call so that changes it makes are tracked, and the display is
 
    // updated afterwards.
 
    var instance = {
 
    var instance = wrapper.CodeMirror = {
 
      getValue: getValue,
 
      setValue: operation(setValue),
 
      getSelection: getSelection,
 
      replaceSelection: operation(replaceSelection),
 
      focus: function(){input.focus(); onFocus(); fastPoll();},
 
      focus: function(){focusInput(); onFocus(); fastPoll();},
 
      setOption: function(option, value) {
 
        options[option] = value;
 
        if (option == "lineNumbers" || option == "gutter") gutterChanged();
 
        if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
 
          operation(gutterChanged)();
 
        else if (option == "mode" || option == "indentUnit") loadMode();
 
        else if (option == "readOnly" && value == "nocursor") input.blur();
 
        else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
 
      },
 
      getOption: function(option) {return options[option];},
 
      undo: operation(undo),
 
      redo: operation(redo),
 
      indentLine: operation(function(n) {if (isLine(n)) indentLine(n, "smart");}),
 
      indentLine: operation(function(n, dir) {
 
        if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
 
      }),
 
      historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
 
      clearHistory: function() {history = new History();},
 
      matchBrackets: operation(function(){matchBrackets(true);}),
 
      getTokenAt: function(pos) {
 
        pos = clipPos(pos);
 
        return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
 
      },
 
      getStateAfter: function(line) {
 
        line = clipLine(line == null ? lines.length - 1: line);
 
        return getStateBefore(line + 1);
 
      },
 
      cursorCoords: function(start){
 
        if (start == null) start = sel.inverted;
 
        return pageCoords(start ? sel.from : sel.to);
 
@@ -132,22 +166,41 @@ var CodeMirror = (function() {
 
      charCoords: function(pos){return pageCoords(clipPos(pos));},
 
      coordsChar: function(coords) {
 
        var off = eltOffset(lineSpace);
 
        var line = Math.min(showingTo - 1, showingFrom + Math.floor(coords.y / lineHeight()));
 
        return clipPos({line: line, ch: charFromX(clipLine(line), coords.x)});
 
        var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
 
        return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
 
      },
 
      getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
 
      markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
 
      setMarker: addGutterMarker,
 
      clearMarker: removeGutterMarker,
 
      markText: operation(markText),
 
      setMarker: operation(addGutterMarker),
 
      clearMarker: operation(removeGutterMarker),
 
      setLineClass: operation(setLineClass),
 
      lineInfo: lineInfo,
 
      addWidget: function(pos, node, scroll) {
 
        var pos = localCoords(clipPos(pos), true);
 
        node.style.top = (showingFrom * lineHeight() + pos.yBot + paddingTop()) + "px";
 
        node.style.left = (pos.x + paddingLeft()) + "px";
 
      addWidget: function(pos, node, scroll, vert, horiz) {
 
        pos = localCoords(clipPos(pos));
 
        var top = pos.yBot, left = pos.x;
 
        node.style.position = "absolute";
 
        code.appendChild(node);
 
        if (vert == "over") top = pos.y;
 
        else if (vert == "near") {
 
          var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
 
              hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
 
          if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
 
            top = pos.y - node.offsetHeight;
 
          if (left + node.offsetWidth > hspace)
 
            left = hspace - node.offsetWidth;
 
        }
 
        node.style.top = (top + paddingTop()) + "px";
 
        node.style.left = node.style.right = "";
 
        if (horiz == "right") {
 
          left = code.clientWidth - node.offsetWidth;
 
          node.style.right = "0px";
 
        } else {
 
          if (horiz == "left") left = 0;
 
          else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
 
          node.style.left = (left + paddingLeft()) + "px";
 
        }
 
        if (scroll)
 
          scrollIntoView(pos.x, pos.yBot, pos.x + node.offsetWidth, pos.yBot + node.offsetHeight);
 
          scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
 
      },
 

	
 
      lineCount: function() {return lines.length;},
 
@@ -171,18 +224,30 @@ var CodeMirror = (function() {
 
      replaceRange: operation(replaceRange),
 
      getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
 

	
 
      coordsFromIndex: function(index) {        
 
        var total = lines.length, pos = 0, line, ch, len;
 
        
 
        for (line = 0; line < total; line++) {
 
          len = lines[line].text.length + 1;
 
          if (pos + len > index) { ch = index - pos; break; }
 
          pos += len;
 
        }
 
        return clipPos({line: line, ch: ch});
 
      },
 

	
 
      operation: function(f){return operation(f)();},
 
      refresh: function(){updateDisplay(true);},
 
      getInputField: function(){return input;},
 
      getWrapperElement: function(){return wrapper;}
 
      getWrapperElement: function(){return wrapper;},
 
      getScrollerElement: function(){return scroller;},
 
      getGutterElement: function(){return gutter;}
 
    };
 

	
 
    function setValue(code) {
 
      history = null;
 
      var top = {line: 0, ch: 0};
 
      updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
 
                  splitLines(code), top, top);
 
      history = new History();
 
      updateInput = true;
 
    }
 
    function getValue(code) {
 
      var text = [];
 
@@ -192,38 +257,70 @@ var CodeMirror = (function() {
 
    }
 

	
 
    function onMouseDown(e) {
 
      // Check whether this is a click in a widget
 
      for (var n = e_target(e); n != wrapper; n = n.parentNode)
 
        if (n.parentNode == code && n != mover) return;
 

	
 
      // First, see if this is a click in the gutter
 
      for (var n = e.target(); n != wrapper; n = n.parentNode)
 
      for (var n = e_target(e); n != wrapper; n = n.parentNode)
 
        if (n.parentNode == gutterText) {
 
          if (options.onGutterClick)
 
            options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom);
 
          return e.stop();
 
            options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
 
          return e_preventDefault(e);
 
        }
 

	
 
      if (gecko && e.button() == 3) onContextMenu(e);
 
      if (e.button() != 1) return;
 
      var start = posFromMouse(e);
 

	
 
      switch (e_button(e)) {
 
      case 3:
 
        if (gecko && !mac) onContextMenu(e);
 
        return;
 
      case 2:
 
        if (start) setCursor(start.line, start.ch, true);
 
        return;
 
      }
 
      // For button 1, if it was clicked inside the editor
 
      // (posFromMouse returning non-null), we have to adjust the
 
      // selection.
 
      var start = posFromMouse(e), last = start, going;
 
      if (!start) {if (e.target() == wrapper) e.stop(); return;}
 
      setCursor(start.line, start.ch, false);
 
      if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
 

	
 
      if (!focused) onFocus();
 
      e.stop();
 
      // And then we have to see if it's a drag event, in which case
 
      // the dragged-over text must be selected.
 
      function end() {
 
        input.focus();
 
        updateInput = true;
 
        move(); up();
 

	
 
      var now = +new Date;
 
      if (lastDoubleClick > now - 400) {
 
        e_preventDefault(e);
 
        return selectLine(start.line);
 
      } else if (lastClick > now - 400) {
 
        lastDoubleClick = now;
 
        e_preventDefault(e);
 
        return selectWordAt(start);
 
      } else { lastClick = now; }
 

	
 
      var last = start, going;
 
      if (dragAndDrop && !posEq(sel.from, sel.to) &&
 
          !posLess(start, sel.from) && !posLess(sel.to, start)) {
 
        // Let the drag handler handle this.
 
        var up = connect(targetDocument, "mouseup", operation(function(e2) {
 
          draggingText = false;
 
          up();
 
          if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
 
            e_preventDefault(e2);
 
            setCursor(start.line, start.ch, true);
 
            focusInput();
 
      }
 
        }), true);
 
        draggingText = true;
 
        return;
 
      }
 
      e_preventDefault(e);
 
      setCursor(start.line, start.ch, true);
 

	
 
      function extend(e) {
 
        var cur = posFromMouse(e, true);
 
        if (cur && !posEq(cur, last)) {
 
          if (!focused) onFocus();
 
          last = cur;
 
          setSelection(start, cur);
 
          setSelectionUser(start, cur);
 
          updateInput = false;
 
          var visible = visibleLines();
 
          if (cur.line >= visible.to || cur.line < visible.from)
 
@@ -231,68 +328,95 @@ var CodeMirror = (function() {
 
        }
 
      }
 

	
 
      var move = connect(document, "mousemove", operation(function(e) {
 
      var move = connect(targetDocument, "mousemove", operation(function(e) {
 
        clearTimeout(going);
 
        e.stop();
 
        e_preventDefault(e);
 
        extend(e);
 
      }), true);
 
      var up = connect(document, "mouseup", operation(function(e) {
 
      var up = connect(targetDocument, "mouseup", operation(function(e) {
 
        clearTimeout(going);
 
        var cur = posFromMouse(e);
 
        if (cur) setSelection(start, cur);
 
        e.stop();
 
        end();
 
        if (cur) setSelectionUser(start, cur);
 
        e_preventDefault(e);
 
        focusInput();
 
        updateInput = true;
 
        move(); up();
 
      }), true);
 
    }
 
    function onDblClick(e) {
 
      var pos = posFromMouse(e);
 
      if (!pos) return;
 
      selectWordAt(pos);
 
      e.stop();
 
    function onDoubleClick(e) {
 
      var start = posFromMouse(e);
 
      if (!start) return;
 
      lastDoubleClick = +new Date;
 
      e_preventDefault(e);
 
      selectWordAt(start);
 
    }
 
    function onDrop(e) {
 
      var pos = posFromMouse(e, true), files = e.e.dataTransfer.files;
 
      e.preventDefault();
 
      var pos = posFromMouse(e, true), files = e.dataTransfer.files;
 
      if (!pos || options.readOnly) return;
 
      if (files && files.length && window.FileReader && window.File) {
 
        var n = files.length, text = Array(n), read = 0;
 
        for (var i = 0; i < n; ++i) loadFile(files[i], i);
 
        function loadFile(file, i) {
 
          var reader = new FileReader;
 
          reader.onload = function() {
 
            text[i] = reader.result;
 
            if (++read == n) replaceRange(text.join(""), clipPos(pos), clipPos(pos));
 
            if (++read == n) {
 
	      pos = clipPos(pos);
 
	      var end = replaceRange(text.join(""), pos, pos);
 
	      setSelectionUser(pos, end);
 
	    }
 
          };
 
          reader.readAsText(file);
 
        }
 
        var n = files.length, text = Array(n), read = 0;
 
        for (var i = 0; i < n; ++i) loadFile(files[i], i);
 
      }
 
      else {
 
        try {
 
          var text = e.e.dataTransfer.getData("Text");
 
          if (text) replaceRange(text, pos, pos);
 
          var text = e.dataTransfer.getData("Text");
 
          if (text) {
 
	    var end = replaceRange(text, pos, pos);
 
	    var curFrom = sel.from, curTo = sel.to;
 
	    setSelectionUser(pos, end);
 
            if (draggingText) replaceRange("", curFrom, curTo);
 
	    focusInput();
 
	  }
 
        }
 
        catch(e){}
 
      }
 
    }
 
    function onDragStart(e) {
 
      var txt = getSelection();
 
      // This will reset escapeElement
 
      htmlEscape(txt);
 
      e.dataTransfer.setDragImage(escapeElement, 0, 0);
 
      e.dataTransfer.setData("Text", txt);
 
    }
 
    function onKeyDown(e) {
 
      if (!focused) onFocus();
 

	
 
      var code = e.e.keyCode;
 
      var code = e.keyCode;
 
      // IE does strange things with escape.
 
      if (ie && code == 27) { e.returnValue = false; }
 
      // Tries to detect ctrl on non-mac, cmd on mac.
 
      var mod = (mac ? e.e.metaKey : e.e.ctrlKey) && !e.e.altKey, anyMod = e.e.ctrlKey || e.e.altKey || e.e.metaKey;
 
      if (code == 16 || e.e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
 
      var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
 
      if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
 
      else shiftSelecting = null;
 
      // First give onKeyEvent option a chance to handle this.
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return;
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
 

	
 
      if (code == 33 || code == 34) {scrollPage(code == 34); return e.stop();} // page up/down
 
      if (mod && (code == 36 || code == 35)) {scrollEnd(code == 36); return e.stop();} // ctrl-home/end
 
      if (mod && code == 65) {selectAll(); return e.stop();} // ctrl-a
 
      if (code == 33 || code == 34) {scrollPage(code == 34); return e_preventDefault(e);} // page up/down
 
      if (mod && ((code == 36 || code == 35) || // ctrl-home/end
 
                  mac && (code == 38 || code == 40))) { // cmd-up/down
 
        scrollEnd(code == 36 || code == 38); return e_preventDefault(e);
 
      }
 
      if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
 
      if (!options.readOnly) {
 
        if (!anyMod && code == 13) {return;} // enter
 
        if (!anyMod && code == 9 && handleTab(e.e.shiftKey)) return e.stop(); // tab
 
        if (mod && code == 90) {undo(); return e.stop();} // ctrl-z
 
        if (mod && ((e.e.shiftKey && code == 90) || code == 89)) {redo(); return e.stop();} // ctrl-shift-z, ctrl-y
 
        if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
 
        if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
 
        if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
 
      }
 
      if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
 

	
 
      // Key id to use in the movementKeys map. We also pass it to
 
      // fastPoll in order to 'self learn'. We need this because
 
@@ -300,52 +424,61 @@ var CodeMirror = (function() {
 
      // its start when it is inverted and a movement key is pressed
 
      // (and later restore it again), shouldn't be used for
 
      // non-movement keys.
 
      curKeyId = (mod ? "c" : "") + code;
 
      if (sel.inverted && movementKeys.hasOwnProperty(curKeyId)) {
 
      curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
 
      if (sel.inverted && movementKeys[curKeyId] === true) {
 
        var range = selRange(input);
 
        if (range) {
 
          reducedSelection = {anchor: range.start};
 
          setSelRange(input, range.start, range.start);
 
        }
 
      }
 
      // Don't save the key as a movementkey unless it had a modifier
 
      if (!mod && !e.altKey) curKeyId = null;
 
      fastPoll(curKeyId);
 
    }
 
    function onKeyUp(e) {
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
 
      if (reducedSelection) {
 
        reducedSelection = null;
 
        updateInput = true;
 
      }
 
      if (e.e.keyCode == 16) shiftSelecting = null;
 
      if (e.keyCode == 16) shiftSelecting = null;
 
    }
 
    function onKeyPress(e) {
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return;
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
 
      if (options.electricChars && mode.electricChars) {
 
        var ch = String.fromCharCode(e.e.charCode == null ? e.e.keyCode : e.e.charCode);
 
        var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
 
        if (mode.electricChars.indexOf(ch) > -1)
 
          setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
 
      }
 
      var code = e.e.keyCode;
 
      var code = e.keyCode;
 
      // Re-stop tab and enter. Necessary on some browsers.
 
      if (code == 13) {handleEnter(); e.stop();}
 
      else if (code == 9 && options.tabMode != "default") e.stop();
 
      if (code == 13) {if (!options.readOnly) handleEnter(); e_preventDefault(e);}
 
      else if (!e.ctrlKey && !e.altKey && !e.metaKey && code == 9 && options.tabMode != "default") e_preventDefault(e);
 
      else fastPoll(curKeyId);
 
    }
 

	
 
    function onFocus() {
 
      if (!focused && options.onFocus) options.onFocus(instance);
 
      if (options.readOnly == "nocursor") return;
 
      if (!focused) {
 
        if (options.onFocus) options.onFocus(instance);
 
      focused = true;
 
      slowPoll();
 
      if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
 
        wrapper.className += " CodeMirror-focused";
 
        if (!leaveInputAlone) prepareInput();
 
      }
 
      slowPoll();
 
      restartBlink();
 
    }
 
    function onBlur() {
 
      if (focused && options.onBlur) options.onBlur(instance);
 
      clearInterval(blinker);
 
      shiftSelecting = null;
 
      if (focused) {
 
        if (options.onBlur) options.onBlur(instance);
 
      focused = false;
 
      wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
 
    }
 
      clearInterval(blinker);
 
      setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
 
    }
 

	
 
    // Replace the range from from to to by the strings in newText.
 
    // Afterwards, set the selection to selFrom, selTo.
 
@@ -367,12 +500,18 @@ var CodeMirror = (function() {
 
        var pos = clipPos({line: change.start + change.old.length - 1,
 
                           ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
 
        updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
 
        updateInput = true;
 
      }
 
    }
 
    function undo() {unredoHelper(history.done, history.undone);}
 
    function redo() {unredoHelper(history.undone, history.done);}
 

	
 
    function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
 
      var recomputeMaxLength = false, maxLineLength = maxLine.length;
 
      for (var i = from.line; i <= to.line; ++i) {
 
        if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
 
      }
 

	
 
      var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
 
      // First adjust the line structure, taking some care to leave highlighting intact.
 
      if (firstLine == lastLine) {
 
@@ -381,24 +520,46 @@ var CodeMirror = (function() {
 
        else {
 
          lastLine = firstLine.split(to.ch, newText[newText.length-1]);
 
          var spliceargs = [from.line + 1, nlines];
 
          firstLine.replace(from.ch, firstLine.text.length, newText[0]);
 
          for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
 
          firstLine.replace(from.ch, null, newText[0]);
 
          for (var i = 1, e = newText.length - 1; i < e; ++i)
 
            spliceargs.push(Line.inheritMarks(newText[i], firstLine));
 
          spliceargs.push(lastLine);
 
          lines.splice.apply(lines, spliceargs);
 
        }
 
      }
 
      else if (newText.length == 1) {
 
        firstLine.replace(from.ch, firstLine.text.length, newText[0] + lastLine.text.slice(to.ch));
 
        firstLine.replace(from.ch, null, newText[0]);
 
        lastLine.replace(null, to.ch, "");
 
        firstLine.append(lastLine);
 
        lines.splice(from.line + 1, nlines);
 
      }
 
      else {
 
        var spliceargs = [from.line + 1, nlines - 1];
 
        firstLine.replace(from.ch, firstLine.text.length, newText[0]);
 
        lastLine.replace(0, to.ch, newText[newText.length-1]);
 
        for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
 
        firstLine.replace(from.ch, null, newText[0]);
 
        lastLine.replace(null, to.ch, newText[newText.length-1]);
 
        for (var i = 1, e = newText.length - 1; i < e; ++i)
 
          spliceargs.push(Line.inheritMarks(newText[i], firstLine));
 
        lines.splice.apply(lines, spliceargs);
 
      }
 

	
 

	
 
      for (var i = from.line, e = i + newText.length; i < e; ++i) {
 
        var l = lines[i].text;
 
        if (l.length > maxLineLength) {
 
          maxLine = l; maxLineLength = l.length; maxWidth = null;
 
          recomputeMaxLength = false;
 
        }
 
      }
 
      if (recomputeMaxLength) {
 
        maxLineLength = 0; maxLine = ""; maxWidth = null;
 
        for (var i = 0, e = lines.length; i < e; ++i) {
 
          var l = lines[i].text;
 
          if (l.length > maxLineLength) {
 
            maxLineLength = l.length; maxLine = l;
 
          }
 
        }
 
      }
 

	
 
      // Add these lines to the work array, so that they will be
 
      // highlighted. Adjust work lines if lines were added/removed.
 
      var newWork = [], lendiff = newText.length - nlines - 1;
 
@@ -407,12 +568,17 @@ var CodeMirror = (function() {
 
        if (task < from.line) newWork.push(task);
 
        else if (task > to.line) newWork.push(task + lendiff);
 
      }
 
      if (newText.length) newWork.push(from.line);
 
      if (newText.length < 5) {
 
        highlightLines(from.line, from.line + newText.length);
 
        newWork.push(from.line + newText.length);
 
      } else {
 
        newWork.push(from.line);
 
      }
 
      work = newWork;
 
      startWorker(100);
 
      // Remember that these lines changed, for updating the display
 
      changes.push({from: from.line, to: to.line + 1, diff: lendiff});
 
      textChanged = true;
 
      textChanged = {from: from, to: to, text: newText};
 

	
 
      // Update the selection
 
      function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
 
@@ -483,7 +649,10 @@ var CodeMirror = (function() {
 
      function p() {
 
        startOperation();
 
        var changed = readInput();
 
        if (changed == "moved" && keyId) movementKeys[keyId] = true;
 
        if (changed && keyId) {
 
          if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
 
          if (changed == "changed") movementKeys[keyId] = false;
 
        }
 
        if (!changed && !missed) {missed = true; poll.set(80, p);}
 
        else {pollingFast = false; slowPoll();}
 
        endOperation();
 
@@ -495,13 +664,12 @@ var CodeMirror = (function() {
 
    // to the data in the editing variable, and updates the editor
 
    // content or cursor if something changed.
 
    function readInput() {
 
      if (leaveInputAlone || !focused) return;
 
      var changed = false, text = input.value, sr = selRange(input);
 
      if (!sr) return false;
 
      var changed = editing.text != text, rs = reducedSelection;
 
      var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
 
      if (reducedSelection && !moved && sel.from.line == 0 && sel.from.ch == 0)
 
        reducedSelection = null;
 
      else if (!moved) return false;
 
      if (!moved && !rs) return false;
 
      if (changed) {
 
        shiftSelecting = reducedSelection = null;
 
        if (options.readOnly) {updateInput = true; return "changed";}
 
@@ -524,13 +692,10 @@ var CodeMirror = (function() {
 
      // so that you can, for example, press shift-up at the start of
 
      // your selection and have the right thing happen.
 
      if (rs) {
 
        from = sr.start == rs.anchor ? to : from;
 
        to = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
 
        if (!posLess(from, to)) {
 
          reducedSelection = null;
 
          sel.inverted = false;
 
          var tmp = from; from = to; to = tmp;
 
        }
 
        var head = sr.start == rs.anchor ? to : from;
 
        var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
 
        if (sel.inverted = posLess(head, tail)) { from = head; to = tail; }
 
        else { reducedSelection = null; from = tail; to = head; }
 
      }
 

	
 
      // In some cases (cursor on same line as before), we don't have
 
@@ -550,8 +715,8 @@ var CodeMirror = (function() {
 
        var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
 
        for (;;) {
 
          c = editing.text.charAt(edend);
 
          if (text.charAt(end) != c) {++end; ++edend; break;}
 
          if (c == "\n") endline--;
 
          if (text.charAt(end) != c) {++end; ++edend; break;}
 
          if (edend <= start || end <= start) break;
 
          --end; --edend;
 
        }
 
@@ -580,22 +745,36 @@ var CodeMirror = (function() {
 
      editing = {text: text, from: from, to: to, start: startch, end: endch};
 
      setSelRange(input, startch, reducedSelection ? startch : endch);
 
    }
 
    function focusInput() {
 
      if (options.readOnly != "nocursor") input.focus();
 
    }
 

	
 
    function scrollEditorIntoView() {
 
      if (!cursor.getBoundingClientRect) return;
 
      var rect = cursor.getBoundingClientRect();
 
      var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
 
      if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
 
    }
 
    function scrollCursorIntoView() {
 
      var cursor = localCoords(sel.inverted ? sel.from : sel.to);
 
      return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
 
    }
 
    function scrollIntoView(x1, y1, x2, y2) {
 
      var pl = paddingLeft(), pt = paddingTop();
 
      var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
 
      y1 += pt; y2 += pt; x1 += pl; x2 += pl;
 
      var screen = wrapper.clientHeight, screentop = wrapper.scrollTop, scrolled = false, result = true;
 
      if (y1 < screentop) {wrapper.scrollTop = Math.max(0, y1 - 10); scrolled = true;}
 
      else if (y2 > screentop + screen) {wrapper.scrollTop = y2 + 10 - screen; scrolled = true;}
 
      var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
 
      if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
 
      else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
 

	
 
      var screenw = wrapper.clientWidth, screenleft = wrapper.scrollLeft;
 
      if (x1 < screenleft) {wrapper.scrollLeft = Math.max(0, x1 - 10); scrolled = true;}
 
      var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
 
      var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
 
      if (x1 < screenleft + gutterw) {
 
        if (x1 < 50) x1 = 0;
 
        scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
 
        scrolled = true;
 
      }
 
      else if (x2 > screenw + screenleft) {
 
        wrapper.scrollLeft = x2 + 10 - screenw;
 
        scroller.scrollLeft = x2 + 10 - screenw;
 
        scrolled = true;
 
        if (x2 > code.clientWidth) result = false;
 
      }
 
@@ -604,15 +783,15 @@ var CodeMirror = (function() {
 
    }
 

	
 
    function visibleLines() {
 
      var lh = lineHeight(), top = wrapper.scrollTop - paddingTop();
 
      var lh = lineHeight(), top = scroller.scrollTop - paddingTop();
 
      return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
 
              to: Math.min(lines.length, Math.ceil((top + wrapper.clientHeight) / lh))};
 
              to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))};
 
    }
 
    // Uses a set of changes plus the current scroll position to
 
    // determine which DOM updates have to be made, and makes the
 
    // updates.
 
    function updateDisplay(changes) {
 
      if (!wrapper.clientWidth) {
 
      if (!scroller.clientWidth) {
 
        showingFrom = showingTo = 0;
 
        return;
 
      }
 
@@ -629,7 +808,7 @@ var CodeMirror = (function() {
 
            intact2.push(range);
 
          else {
 
            if (change.from > range.from)
 
              intact2.push({from: range.from, to: change.from, domStart: range.domStart})
 
              intact2.push({from: range.from, to: change.from, domStart: range.domStart});
 
            if (change.to < range.to)
 
              intact2.push({from: change.to + diff, to: range.to + diff,
 
                            domStart: range.domStart + (change.to - range.from)});
 
@@ -659,6 +838,7 @@ var CodeMirror = (function() {
 
      if (domPos != domEnd || pos != to) {
 
        changedLines += Math.abs(to - pos);
 
        updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
 
        if (to - pos != domEnd - domPos) gutterDirty = true;
 
      }
 

	
 
      if (!updates.length) return;
 
@@ -674,13 +854,23 @@ var CodeMirror = (function() {
 

	
 
      // Position the mover div to align with the lines it's supposed
 
      // to be showing (which will cover the visible display)
 
      var different = from != showingFrom || to != showingTo || lastHeight != wrapper.clientHeight;
 
      var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
 
      showingFrom = from; showingTo = to;
 
      mover.style.top = (from * lineHeight()) + "px";
 
      if (different) {
 
        lastHeight = wrapper.clientHeight;
 
        lastHeight = scroller.clientHeight;
 
        code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
 
        updateGutter();
 
      }
 
      if (different || gutterDirty) updateGutter();
 

	
 
      if (maxWidth == null) maxWidth = stringWidth(maxLine);
 
      if (maxWidth > scroller.clientWidth) {
 
        lineSpace.style.width = maxWidth + "px";
 
        // Needed to prevent odd wrapping/hiding of widgets placed in here.
 
        code.style.width = "";
 
        code.style.width = scroller.scrollWidth + "px";
 
      } else {
 
        lineSpace.style.width = code.style.width = "";
 
      }
 

	
 
      // Since this is all rather error prone, it is honoured with the
 
@@ -712,7 +902,7 @@ var CodeMirror = (function() {
 
      // there .innerHTML on PRE nodes is dumb, and discards
 
      // whitespace.
 
      var sfrom = sel.from.line, sto = sel.to.line, off = 0,
 
          scratch = badInnerHTML && document.createElement("div");
 
          scratch = badInnerHTML && targetDocument.createElement("div");
 
      for (var i = 0, e = updates.length; i < e; ++i) {
 
        var rec = updates[i];
 
        var extra = (rec.to - rec.from) - rec.domSize;
 
@@ -722,7 +912,7 @@ var CodeMirror = (function() {
 
            lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
 
        else if (extra) {
 
          for (var j = Math.max(0, extra); j > 0; --j)
 
            lineDiv.insertBefore(document.createElement("pre"), nodeAfter);
 
            lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
 
          for (var j = Math.max(0, -extra); j > 0; --j)
 
            lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
 
        }
 
@@ -753,10 +943,10 @@ var CodeMirror = (function() {
 

	
 
    function updateGutter() {
 
      if (!options.gutter && !options.lineNumbers) return;
 
      var hText = mover.offsetHeight, hEditor = wrapper.clientHeight;
 
      var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
 
      gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
 
      var html = [];
 
      for (var i = showingFrom; i < showingTo; ++i) {
 
      for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) {
 
        var marker = lines[i].gutterMarker;
 
        var text = options.lineNumbers ? i + options.firstLineNumber : null;
 
        if (marker && marker.text)
 
@@ -769,37 +959,43 @@ var CodeMirror = (function() {
 
      gutterText.innerHTML = html.join("");
 
      var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
 
      while (val.length + pad.length < minwidth) pad += "\u00a0";
 
      if (pad) firstNode.insertBefore(document.createTextNode(pad), firstNode.firstChild);
 
      if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
 
      gutter.style.display = "";
 
      lineSpace.style.marginLeft = gutter.offsetWidth + "px";
 
      gutterDirty = false;
 
    }
 
    function updateCursor() {
 
      var head = sel.inverted ? sel.from : sel.to;
 
      var x = charX(head.line, head.ch) + "px", y = (head.line - showingFrom) * lineHeight() + "px";
 
      inputDiv.style.top = y; inputDiv.style.left = x;
 
      var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
 
      var x = charX(head.line, head.ch);
 
      var top = head.line * lh - scroller.scrollTop;
 
      inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
 
      inputDiv.style.left = (x - scroller.scrollLeft) + "px";
 
      if (posEq(sel.from, sel.to)) {
 
        cursor.style.top = y; cursor.style.left = x;
 
        cursor.style.top = (head.line - showingFrom) * lh + "px";
 
        cursor.style.left = x + "px";
 
        cursor.style.display = "";
 
      }
 
      else cursor.style.display = "none";
 
    }
 

	
 
    function setSelectionUser(from, to) {
 
      var sh = shiftSelecting && clipPos(shiftSelecting);
 
      if (sh) {
 
        if (posLess(sh, from)) from = sh;
 
        else if (posLess(to, sh)) to = sh;
 
      }
 
      setSelection(from, to);
 
    }
 
    // Update the selection. Last two args are only used by
 
    // updateLines, since they have to be expressed in the line
 
    // numbers before the update.
 
    function setSelection(from, to, oldFrom, oldTo) {
 
      if (posEq(sel.from, from) && posEq(sel.to, to)) return;
 
      var sh = shiftSelecting && clipPos(shiftSelecting);
 
      if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
 
      if (sh) {
 
        if (posLess(sh, from)) from = sh;
 
        else if (posLess(to, sh)) to = sh;
 
      }
 

	
 
      var startEq = posEq(sel.to, to), endEq = posEq(sel.from, from);
 
      if (posEq(from, to)) sel.inverted = false;
 
      else if (startEq && !endEq) sel.inverted = true;
 
      else if (endEq && !startEq) sel.inverted = false;
 
      else if (posEq(from, sel.to)) sel.inverted = false;
 
      else if (posEq(to, sel.from)) sel.inverted = true;
 

	
 
      // Some ugly logic used to only mark the lines that actually did
 
      // see a change in selection as changed, rather than the whole
 
@@ -829,9 +1025,9 @@ var CodeMirror = (function() {
 
      sel.from = from; sel.to = to;
 
      selectionChanged = true;
 
    }
 
    function setCursor(line, ch) {
 
    function setCursor(line, ch, user) {
 
      var pos = clipPos({line: line, ch: ch || 0});
 
      setSelection(pos, pos);
 
      (user ? setSelectionUser : setSelection)(pos, pos);
 
    }
 

	
 
    function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
 
@@ -845,11 +1041,12 @@ var CodeMirror = (function() {
 
    }
 

	
 
    function scrollPage(down) {
 
      var linesPerPage = Math.floor(wrapper.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
 
      setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch);
 
      var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
 
      setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
 
    }
 
    function scrollEnd(top) {
 
      setCursor(top ? 0 : lines.length - 1);
 
      var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length};
 
      setSelectionUser(pos, pos);
 
    }
 
    function selectAll() {
 
      var endLine = lines.length - 1;
 
@@ -859,8 +1056,11 @@ var CodeMirror = (function() {
 
      var line = lines[pos.line].text;
 
      var start = pos.ch, end = pos.ch;
 
      while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
 
      while (end < line.length - 1 && /\w/.test(line.charAt(end))) ++end;
 
      setSelection({line: pos.line, ch: start}, {line: pos.line, ch: end});
 
      while (end < line.length && /\w/.test(line.charAt(end))) ++end;
 
      setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
 
    }
 
    function selectLine(line) {
 
      setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
 
    }
 
    function handleEnter() {
 
      replaceSelection("\n", "end");
 
@@ -868,12 +1068,17 @@ var CodeMirror = (function() {
 
        indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
 
    }
 
    function handleTab(shift) {
 
      function indentSelected(mode) {
 
        if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
 
        var e = sel.to.line - (sel.to.ch ? 0 : 1);
 
        for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
 
      }
 
      shiftSelecting = null;
 
      switch (options.tabMode) {
 
      case "default":
 
        return false;
 
      case "indent":
 
        for (var i = sel.from.line, e = sel.to.line; i <= e; ++i) indentLine(i, "smart");
 
        indentSelected("smart");
 
        break;
 
      case "classic":
 
        if (posEq(sel.from, sel.to)) {
 
@@ -882,11 +1087,15 @@ var CodeMirror = (function() {
 
          break;
 
        }
 
      case "shift":
 
        for (var i = sel.from.line, e = sel.to.line; i <= e; ++i) indentLine(i, shift ? "subtract" : "add");
 
        indentSelected(shift ? "subtract" : "add");
 
        break;
 
      }
 
      return true;
 
    }
 
    function smartHome() {
 
      var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
 
      setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
 
    }
 

	
 
    function indentLine(n, how) {
 
      if (how == "smart") {
 
@@ -924,21 +1133,20 @@ var CodeMirror = (function() {
 
      for (var i = 0, l = lines.length; i < l; ++i)
 
        lines[i].stateAfter = null;
 
      work = [0];
 
      startWorker();
 
    }
 
    function gutterChanged() {
 
      var visible = options.gutter || options.lineNumbers;
 
      gutter.style.display = visible ? "" : "none";
 
      if (visible) updateGutter();
 
      if (visible) gutterDirty = true;
 
      else lineDiv.parentNode.style.marginLeft = 0;
 
    }
 

	
 
    function markText(from, to, className) {
 
      from = clipPos(from); to = clipPos(to);
 
      var accum = [];
 
      var set = [];
 
      function add(line, from, to, className) {
 
        var line = lines[line], mark = line.addMark(from, to, className);
 
        mark.line = line;
 
        accum.push(mark);
 
        mark = lines[line].addMark(from, to, className, set);
 
      }
 
      if (from.line == to.line) add(from.line, from.ch, to.ch, className);
 
      else {
 
@@ -948,30 +1156,51 @@ var CodeMirror = (function() {
 
        add(to.line, 0, to.ch, className);
 
      }
 
      changes.push({from: from.line, to: to.line + 1});
 
      return function() {
 
        var start, end;
 
        for (var i = 0; i < accum.length; ++i) {
 
          var mark = accum[i], found = indexOf(lines, mark.line);
 
          mark.line.removeMark(mark);
 
          if (found > -1) {
 
            if (start == null) start = found;
 
            end = found;
 
      return new TextMarker(set);
 
    }
 

	
 
    function TextMarker(set) { this.set = set; }
 
    TextMarker.prototype.clear = operation(function() {
 
      for (var i = 0, e = this.set.length; i < e; ++i) {
 
        var mk = this.set[i].marked;
 
        for (var j = 0; j < mk.length; ++j) {
 
          if (mk[j].set == this.set) mk.splice(j--, 1);
 
          }
 
        }
 
        if (start != null) changes.push({from: start, to: end + 1});
 
      // We don't know the exact lines that changed. Refreshing is
 
      // cheaper than finding them.
 
      changes.push({from: 0, to: lines.length});
 
    });
 
    TextMarker.prototype.find = function() {
 
      var from, to;
 
      for (var i = 0, e = this.set.length; i < e; ++i) {
 
        var line = this.set[i], mk = line.marked;
 
        for (var j = 0; j < mk.length; ++j) {
 
          var mark = mk[j];
 
          if (mark.set == this.set) {
 
            if (mark.from != null || mark.to != null) {
 
              var found = indexOf(lines, line);
 
              if (found > -1) {
 
                if (mark.from != null) from = {line: found, ch: mark.from};
 
                if (mark.to != null) to = {line: found, ch: mark.to};
 
              }
 
            }
 
          }
 
        }
 
      }
 
      return {from: from, to: to};
 
      };
 
    }
 

	
 
    function addGutterMarker(line, text, className) {
 
      if (typeof line == "number") line = lines[clipLine(line)];
 
      line.gutterMarker = {text: text, style: className};
 
      updateGutter();
 
      gutterDirty = true;
 
      return line;
 
    }
 
    function removeGutterMarker(line) {
 
      if (typeof line == "number") line = lines[clipLine(line)];
 
      line.gutterMarker = null;
 
      updateGutter();
 
      gutterDirty = true;
 
    }
 
    function setLineClass(line, className) {
 
      if (typeof line == "number") {
 
@@ -982,8 +1211,10 @@ var CodeMirror = (function() {
 
        var no = indexOf(lines, line);
 
        if (no == -1) return null;
 
      }
 
      if (line.className != className) {
 
      line.className = className;
 
      changes.push({from: no, to: no + 1});
 
      }
 
      return line;
 
    }
 

	
 
@@ -1001,35 +1232,44 @@ var CodeMirror = (function() {
 
      return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
 
    }
 

	
 
    function stringWidth(str) {
 
      measure.innerHTML = "<pre><span>x</span></pre>";
 
      measure.firstChild.firstChild.firstChild.nodeValue = str;
 
      return measure.firstChild.firstChild.offsetWidth || 10;
 
    }
 
    // These are used to go from pixel positions to character
 
    // positions, taking tabs into account.
 
    // positions, taking varying character widths into account.
 
    function charX(line, pos) {
 
      var text = lines[line].text, span = measure.firstChild;
 
      if (text.lastIndexOf("\t", pos) == -1) return pos * charWidth();
 
      var old = span.firstChild.nodeValue;
 
      try {
 
        span.firstChild.nodeValue = text.slice(0, pos);
 
        return span.offsetWidth;
 
      } finally {span.firstChild.nodeValue = old;}
 
      if (pos == 0) return 0;
 
      measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
 
      return measure.firstChild.firstChild.offsetWidth;
 
    }
 
    function charFromX(line, x) {
 
      var text = lines[line].text, cw = charWidth();
 
      if (x <= 0) return 0;
 
      if (text.indexOf("\t") == -1) return Math.min(text.length, Math.round(x / cw));
 
      var mspan = measure.firstChild, mtext = mspan.firstChild, old = mtext.nodeValue;
 
      try {
 
        mtext.nodeValue = text;
 
        var from = 0, fromX = 0, to = text.length, toX = mspan.offsetWidth;
 
      var lineObj = lines[line], text = lineObj.text;
 
      function getX(len) {
 
        measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
 
        return measure.firstChild.firstChild.offsetWidth;
 
      }
 
      var from = 0, fromX = 0, to = text.length, toX;
 
      // Guess a suitable upper bound for our search.
 
      var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
 
      for (;;) {
 
        var estX = getX(estimated);
 
        if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
 
        else {toX = estX; to = estimated; break;}
 
      }
 
        if (x > toX) return to;
 
      // Try to guess a suitable lower bound as well.
 
      estimated = Math.floor(to * 0.8); estX = getX(estimated);
 
      if (estX < x) {from = estimated; fromX = estX;}
 
      // Do a binary search between these bounds.
 
        for (;;) {
 
          if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
 
          var middle = Math.ceil((from + to) / 2);
 
          mtext.nodeValue = text.slice(0, middle);
 
          var curX = mspan.offsetWidth;
 
          if (curX > x) {to = middle; toX = curX;}
 
          else {from = middle; fromX = curX;}
 
        var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
 
        if (middleX > x) {to = middle; toX = middleX;}
 
        else {from = middle; fromX = middleX;}
 
        }
 
      } finally {mtext.nodeValue = old;}
 
    }
 

	
 
    function localCoords(pos, inLineWrap) {
 
@@ -1043,45 +1283,61 @@ var CodeMirror = (function() {
 

	
 
    function lineHeight() {
 
      var nlines = lineDiv.childNodes.length;
 
      if (nlines) return lineDiv.offsetHeight / nlines;
 
      else return measure.firstChild.offsetHeight || 1;
 
      if (nlines) return (lineDiv.offsetHeight / nlines) || 1;
 
      measure.innerHTML = "<pre>x</pre>";
 
      return measure.firstChild.offsetHeight || 1;
 
    }
 
    function charWidth() {return (measure.firstChild.offsetWidth || 320) / 40;}
 
    function paddingTop() {return lineSpace.offsetTop;}
 
    function paddingLeft() {return lineSpace.offsetLeft;}
 

	
 
    function posFromMouse(e, liberal) {
 
      var off = eltOffset(lineSpace),
 
          x = e.pageX() - off.left,
 
          y = e.pageY() - off.top;
 
      if (!liberal && e.target() != lineSpace.parentNode && !(e.target() == wrapper && y > (lines.length * lineHeight())))
 
        for (var n = e.target(); n != lineDiv && n != cursor; n = n.parentNode)
 
          if (!n || n == wrapper) return null;
 
      var line = showingFrom + Math.floor(y / lineHeight());
 
      return clipPos({line: line, ch: charFromX(clipLine(line), x)});
 
      var offW = eltOffset(scroller, true), x, y;
 
      // Fails unpredictably on IE[67] when mouse is dragged around quickly.
 
      try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
 
      // This is a mess of a heuristic to try and determine whether a
 
      // scroll-bar was clicked or not, and to return null if one was
 
      // (and !liberal).
 
      if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
 
        return null;
 
      var offL = eltOffset(lineSpace, true);
 
      var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
 
      return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
 
    }
 
    function onContextMenu(e) {
 
      var pos = posFromMouse(e);
 
      if (!pos || window.opera) return; // Opera is difficult.
 
      if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
 
        setCursor(pos.line, pos.ch);
 
        operation(setCursor)(pos.line, pos.ch);
 

	
 
      var oldCSS = input.style.cssText;
 
      input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.pageY() - 1) +
 
        "px; left: " + (e.pageX() - 1) + "px; z-index: 1000; background: white; " +
 
        "border-width: 0; outline: none; overflow: hidden;";
 
      inputDiv.style.position = "absolute";
 
      input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
 
        "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
 
        "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
 
      leaveInputAlone = true;
 
      var val = input.value = getSelection();
 
      input.focus();
 
      setSelRange(input, 0, val.length);
 
      if (gecko) e.stop();
 
      leaveInputAlone = true;
 
      setTimeout(function() {
 
        if (input.value != val) operation(replaceSelection)(input.value, "end");
 
      focusInput();
 
      setSelRange(input, 0, input.value.length);
 
      function rehide() {
 
        var newVal = splitLines(input.value).join("\n");
 
        if (newVal != val) operation(replaceSelection)(newVal, "end");
 
        inputDiv.style.position = "relative";
 
        input.style.cssText = oldCSS;
 
        leaveInputAlone = false;
 
        prepareInput();
 
        slowPoll();
 
      }, 50);
 
      }
 

	
 
      if (gecko) {
 
        e_stop(e);
 
        var mouseup = connect(window, "mouseup", function() {
 
          mouseup();
 
          setTimeout(rehide, 20);
 
        }, true);
 
      }
 
      else {
 
        setTimeout(rehide, 50);
 
      }
 
    }
 

	
 
    // Cursor-blinking
 
@@ -1120,19 +1376,18 @@ var CodeMirror = (function() {
 
          }
 
        }
 
      }
 
      for (var i = head.line, e = forward ? Math.min(i + 50, lines.length) : Math.max(0, i - 50); i != e; i+=d) {
 
      for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) {
 
        var line = lines[i], first = i == head.line;
 
        var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
 
        if (found) {
 
        if (found) break;
 
      }
 
      if (!found) found = {pos: null, match: false};
 
          var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
 
          var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
 
              two = markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
 
          var clear = operation(function(){one(); two();});
 
          two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
 
      var clear = operation(function(){one.clear(); two && two.clear();});
 
          if (autoclear) setTimeout(clear, 800);
 
          else bracketHighlighted = clear;
 
          break;
 
        }
 
      }
 
    }
 

	
 
    // Finds the line to start with when starting a parse. Tries to
 
@@ -1148,7 +1403,7 @@ var CodeMirror = (function() {
 
        if (line.stateAfter) return search;
 
        var indented = line.indentation();
 
        if (minline == null || minindent > indented) {
 
          minline = search;
 
          minline = search - 1;
 
          minindent = indented;
 
        }
 
      }
 
@@ -1163,11 +1418,21 @@ var CodeMirror = (function() {
 
        line.highlight(mode, state);
 
        line.stateAfter = copyState(mode, state);
 
      }
 
      if (!lines[n].stateAfter) work.push(n);
 
      changes.push({from: start, to: n});
 
      if (n < lines.length && !lines[n].stateAfter) work.push(n);
 
      return state;
 
    }
 
    function highlightLines(start, end) {
 
      var state = getStateBefore(start);
 
      for (var i = start; i < end; ++i) {
 
        var line = lines[i];
 
        line.highlight(mode, state);
 
        line.stateAfter = copyState(mode, state);
 
      }
 
    }
 
    function highlightWorker() {
 
      var end = +new Date + options.workTime;
 
      var foundWork = work.length;
 
      while (work.length) {
 
        if (!lines[showingFrom].stateAfter) var task = showingFrom;
 
        else var task = work.pop();
 
@@ -1176,20 +1441,29 @@ var CodeMirror = (function() {
 
        if (state) state = copyState(mode, state);
 
        else state = startState(mode);
 

	
 
        var unchanged = 0, compare = mode.compareStates, realChange = false;
 
        for (var i = start, l = lines.length; i < l; ++i) {
 
          var line = lines[i], hadState = line.stateAfter;
 
          if (+new Date > end) {
 
            work.push(i);
 
            startWorker(options.workDelay);
 
            changes.push({from: task, to: i});
 
            if (realChange) changes.push({from: task, to: i + 1});
 
            return;
 
          }
 
          var changed = line.highlight(mode, state);
 
          if (changed) realChange = true;
 
          line.stateAfter = copyState(mode, state);
 
          if (hadState && !changed && line.text) break;
 
          if (compare) {
 
            if (hadState && compare(hadState, state)) break;
 
          } else {
 
            if (changed !== false || !hadState) unchanged = 0;
 
            else if (++unchanged > 3) break;
 
        }
 
        changes.push({from: task, to: i});
 
        }
 
        if (realChange) changes.push({from: task, to: i + 1});
 
      }
 
      if (foundWork && options.onHighlightComplete)
 
        options.onHighlightComplete(instance);
 
    }
 
    function startWorker(time) {
 
      if (!work.length) return;
 
@@ -1207,24 +1481,29 @@ var CodeMirror = (function() {
 
      var reScroll = false;
 
      if (selectionChanged) reScroll = !scrollCursorIntoView();
 
      if (changes.length) updateDisplay(changes);
 
      else if (selectionChanged) updateCursor();
 
      else {
 
        if (selectionChanged) updateCursor();
 
        if (gutterDirty) updateGutter();
 
      }
 
      if (reScroll) scrollCursorIntoView();
 
      if (selectionChanged) restartBlink();
 
      if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
 

	
 
      // updateInput can be set to a boolean value to force/prevent an
 
      // update.
 
      if (!leaveInputAlone && (updateInput === true || (updateInput !== false && selectionChanged)))
 
      if (focused && !leaveInputAlone &&
 
          (updateInput === true || (updateInput !== false && selectionChanged)))
 
        prepareInput();
 

	
 
      if (selectionChanged && options.onCursorActivity)
 
        options.onCursorActivity(instance);
 
      if (textChanged && options.onChange)
 
        options.onChange(instance);
 
      if (selectionChanged && options.matchBrackets)
 
        setTimeout(operation(function() {
 
          if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
 
          matchBrackets(false);
 
        }), 20);
 
      var tc = textChanged; // textChanged can be reset by cursoractivity callback
 
      if (selectionChanged && options.onCursorActivity)
 
        options.onCursorActivity(instance);
 
      if (tc && options.onChange && instance)
 
        options.onChange(instance, tc);
 
    }
 
    var nestedOperation = 0;
 
    function operation(f) {
 
@@ -1259,6 +1538,7 @@ var CodeMirror = (function() {
 
              var newmatch = line.match(query);
 
              if (newmatch) match = newmatch;
 
              else break;
 
              start++;
 
            }
 
          }
 
          else {
 
@@ -1338,9 +1618,21 @@ var CodeMirror = (function() {
 
      },
 

	
 
      from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
 
      to: function() {if (this.atOccurrence) return copyPos(this.pos.to);}
 
      to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
 

	
 
      replace: function(newText) {
 
        var self = this;
 
        if (this.atOccurrence)
 
          operation(function() {
 
            self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
 
          })();
 
      }
 
    };
 

	
 
    for (var ext in extensions)
 
      if (extensions.propertyIsEnumerable(ext) &&
 
          !instance.propertyIsEnumerable(ext))
 
        instance[ext] = extensions[ext];
 
    return instance;
 
  } // (end of function CodeMirror)
 

	
 
@@ -1348,6 +1640,7 @@ var CodeMirror = (function() {
 
  CodeMirror.defaults = {
 
    value: "",
 
    mode: null,
 
    theme: "default",
 
    indentUnit: 2,
 
    indentWithTabs: false,
 
    tabMode: "classic",
 
@@ -1356,17 +1649,21 @@ var CodeMirror = (function() {
 
    onKeyEvent: null,
 
    lineNumbers: false,
 
    gutter: false,
 
    fixedGutter: false,
 
    firstLineNumber: 1,
 
    readOnly: false,
 
    smartHome: true,
 
    onChange: null,
 
    onCursorActivity: null,
 
    onGutterClick: null,
 
    onHighlightComplete: null,
 
    onFocus: null, onBlur: null, onScroll: null,
 
    matchBrackets: false,
 
    workTime: 100,
 
    workDelay: 200,
 
    undoDepth: 40,
 
    tabindex: null
 
    tabindex: null,
 
    document: window.document
 
  };
 

	
 
  // Known modes, by name and by MIME
 
@@ -1383,15 +1680,15 @@ var CodeMirror = (function() {
 
      spec = mimeModes[spec];
 
    if (typeof spec == "string")
 
      var mname = spec, config = {};
 
    else
 
    else if (spec != null)
 
      var mname = spec.name, config = spec;
 
    var mfactory = modes[mname];
 
    if (!mfactory) {
 
      if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
 
      return CodeMirror.getMode(options, "text/plain");
 
    }
 
    return mfactory(options, config);
 
  }
 
    return mfactory(options, config || {});
 
  };
 
  CodeMirror.listModes = function() {
 
    var list = [];
 
    for (var m in modes)
 
@@ -1401,10 +1698,15 @@ var CodeMirror = (function() {
 
  CodeMirror.listMIMEs = function() {
 
    var list = [];
 
    for (var m in mimeModes)
 
      if (mimeModes.propertyIsEnumerable(m)) list.push(m);
 
      if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
 
    return list;
 
  };
 

	
 
  var extensions = {};
 
  CodeMirror.defineExtension = function(name, func) {
 
    extensions[name] = func;
 
  };
 

	
 
  CodeMirror.fromTextArea = function(textarea, options) {
 
    if (!options) options = {};
 
    options.value = textarea.value;
 
@@ -1484,7 +1786,7 @@ var CodeMirror = (function() {
 
      if (ok) {++this.pos; return ch;}
 
    },
 
    eatWhile: function(match) {
 
      var start = this.start;
 
      var start = this.pos;
 
      while (this.eat(match)){}
 
      return this.pos > start;
 
    },
 
@@ -1517,6 +1819,7 @@ var CodeMirror = (function() {
 
    },
 
    current: function(){return this.string.slice(this.start, this.pos);}
 
  };
 
  CodeMirror.StringStream = StringStream;
 

	
 
  // Line objects. These hold state related to a line, including
 
  // highlighting info (the styles array).
 
@@ -1526,10 +1829,23 @@ var CodeMirror = (function() {
 
    this.text = text;
 
    this.marked = this.gutterMarker = this.className = null;
 
  }
 
  Line.inheritMarks = function(text, orig) {
 
    var ln = new Line(text), mk = orig.marked;
 
    if (mk) {
 
      for (var i = 0; i < mk.length; ++i) {
 
        if (mk[i].to == null) {
 
          var newmk = ln.marked || (ln.marked = []), mark = mk[i];
 
          newmk.push({from: null, to: null, style: mark.style, set: mark.set});
 
          mark.set.push(ln);
 
        }
 
      }
 
    }
 
    return ln;
 
  }
 
  Line.prototype = {
 
    // Replace a piece of a line, keeping the styles around it intact.
 
    replace: function(from, to, text) {
 
      var st = [], mk = this.marked;
 
    replace: function(from, to_, text) {
 
      var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
 
      copyStyles(0, from, this.styles, st);
 
      if (text) st.push(text, null);
 
      copyStyles(to, this.text.length, this.styles, st);
 
@@ -1538,39 +1854,86 @@ var CodeMirror = (function() {
 
      this.stateAfter = null;
 
      if (mk) {
 
        var diff = text.length - (to - from), end = this.text.length;
 
        function fix(n) {return n <= Math.min(to, to + diff) ? n : n + diff;}
 
        var changeStart = Math.min(from, from + diff);
 
        for (var i = 0; i < mk.length; ++i) {
 
          var mark = mk[i], del = false;
 
          if (mark.from >= end) del = true;
 
          else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
 
          if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
 
          if (mark.from != null && mark.from >= end) del = true;
 
          else {
 
            if (mark.from != null && mark.from >= from) {
 
              mark.from += diff;
 
              if (mark.from <= 0) mark.from = from == null ? null : 0;
 
            }
 
            else if (to_ == null) mark.to = null;
 
            if (mark.to != null && mark.to > from) {
 
              mark.to += diff;
 
              if (mark.to < 0) del = true;
 
            }
 
          }
 
          if (del || (mark.from != null && mark.to != null && mark.from >= mark.to)) mk.splice(i--, 1);
 
        }
 
      }
 
    },
 
    // Split a line in two, again keeping styles intact.
 
    // Split a part off a line, keeping styles and markers intact.
 
    split: function(pos, textBefore) {
 
      var st = [textBefore, null];
 
      var st = [textBefore, null], mk = this.marked;
 
      copyStyles(pos, this.text.length, this.styles, st);
 
      return new Line(textBefore + this.text.slice(pos), st);
 
      var taken = new Line(textBefore + this.text.slice(pos), st);
 
      if (mk) {
 
        for (var i = 0; i < mk.length; ++i) {
 
          var mark = mk[i];
 
          if (mark.to > pos || mark.to == null) {
 
            if (!taken.marked) taken.marked = [];
 
            taken.marked.push({
 
              from: mark.from < pos || mark.from == null ? null : mark.from - pos + textBefore.length,
 
              to: mark.to == null ? null : mark.to - pos + textBefore.length,
 
              style: mark.style, set: mark.set
 
            });
 
            mark.set.push(taken);
 
          }
 
        }
 
      }
 
      return taken;
 
    },
 
    addMark: function(from, to, style) {
 
      var mk = this.marked, mark = {from: from, to: to, style: style};
 
    append: function(line) {
 
      if (!line.text.length) return;
 
      var mylen = this.text.length, mk = line.marked;
 
      this.text += line.text;
 
      copyStyles(0, line.text.length, line.styles, this.styles);
 
      if (mk && mk.length) {
 
        var mymk = this.marked || (this.marked = []);
 
        for (var i = 0; i < mymk.length; ++i)
 
          if (mymk[i].to == null) mymk[i].to = mylen;
 
        outer: for (var i = 0; i < mk.length; ++i) {
 
          var mark = mk[i];
 
          if (!mark.from) {
 
            for (var j = 0; j < mymk.length; ++j) {
 
              var mymark = mymk[j];
 
              if (mymark.to == mylen && mymark.set == mark.set) {
 
                mymark.to = mark.to == null ? null : mark.to + mylen;
 
                continue outer;
 
              }
 
            }
 
          }
 
          mymk.push(mark);
 
          mark.set.push(this);
 
          mark.from += mylen;
 
          if (mark.to != null) mark.to += mylen;
 
        }
 
      }
 
    },
 
    addMark: function(from, to, style, set) {
 
      set.push(this);
 
      if (this.marked == null) this.marked = [];
 
      this.marked.push(mark);
 
      this.marked.sort(function(a, b){return a.from - b.from;});
 
      return mark;
 
    },
 
    removeMark: function(mark) {
 
      var mk = this.marked;
 
      if (!mk) return;
 
      for (var i = 0; i < mk.length; ++i)
 
        if (mk[i] == mark) {mk.splice(i, 1); break;}
 
      this.marked.push({from: from, to: to, style: style, set: set});
 
      this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
 
    },
 
    // Run the given mode's parser over a line, update the styles
 
    // array, which contains alternating fragments of text and CSS
 
    // classes.
 
    highlight: function(mode, state) {
 
      var stream = new StringStream(this.text), st = this.styles, pos = 0, changed = false;
 
      var stream = new StringStream(this.text), st = this.styles, pos = 0;
 
      var changed = false, curWord = st[0], prevWord;
 
      if (this.text == "" && mode.blankLine) mode.blankLine(state);
 
      while (!stream.eol()) {
 
        var style = mode.token(stream, state);
 
        var substr = this.text.slice(stream.start, stream.pos);
 
@@ -1578,8 +1941,9 @@ var CodeMirror = (function() {
 
        if (pos && st[pos-1] == style)
 
          st[pos-2] += substr;
 
        else if (substr) {
 
          if (!changed && st[pos] != substr || st[pos+1] != style) changed = true;
 
          if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
 
          st[pos++] = substr; st[pos++] = style;
 
          prevWord = curWord; curWord = st[pos];
 
        }
 
        // Give up when line is ridiculously long
 
        if (stream.pos > 5000) {
 
@@ -1588,7 +1952,11 @@ var CodeMirror = (function() {
 
        }
 
      }
 
      if (st.length != pos) {st.length = pos; changed = true;}
 
      return changed;
 
      if (pos && st[pos-2] != prevWord) changed = true;
 
      // Short lines with simple highlights return null, and are
 
      // counted as changed by the driver because they are likely to
 
      // highlight the same way in various contexts.
 
      return changed || (st.length < 5 && this.text.length < 10 ? null : false);
 
    },
 
    // Fetch the parser token for a given character. Useful for hacks
 
    // that want to inspect the mode state (say, for completion).
 
@@ -1607,7 +1975,7 @@ var CodeMirror = (function() {
 
    indentation: function() {return countColumn(this.text);},
 
    // Produces an HTML fragment for the line, taking selection,
 
    // marking, and highlighting into account.
 
    getHTML: function(sfrom, sto, includePre) {
 
    getHTML: function(sfrom, sto, includePre, endAt) {
 
      var html = [];
 
      if (includePre)
 
        html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
 
@@ -1618,11 +1986,18 @@ var CodeMirror = (function() {
 
      }
 
      var st = this.styles, allText = this.text, marked = this.marked;
 
      if (sfrom == sto) sfrom = null;
 
      var len = allText.length;
 
      if (endAt != null) len = Math.min(endAt, len);
 

	
 
      if (!allText)
 
      if (!allText && endAt == null)
 
        span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
 
      else if (!marked && sfrom == null)
 
        for (var i = 0, e = st.length; i < e; i+=2) span(st[i], st[i+1]);
 
        for (var i = 0, ch = 0; ch < len; i+=2) {
 
          var str = st[i], style = st[i+1], l = str.length;
 
          if (ch + l > len) str = str.slice(0, len - ch);
 
          ch += l;
 
          span(str, style && "cm-" + style);
 
        }
 
      else {
 
        var pos = 0, i = 0, text = "", style, sg = 0;
 
        var markpos = -1, mark = null;
 
@@ -1633,8 +2008,8 @@ var CodeMirror = (function() {
 
          }
 
        }
 
        nextMark();        
 
        while (pos < allText.length) {
 
          var upto = allText.length;
 
        while (pos < len) {
 
          var upto = len;
 
          var extraStyle = "";
 
          if (sfrom != null) {
 
            if (sfrom > pos) upto = sfrom;
 
@@ -1653,12 +2028,12 @@ var CodeMirror = (function() {
 
          }
 
          for (;;) {
 
            var end = pos + text.length;
 
            var apliedStyle = style;
 
            if (extraStyle) apliedStyle = style ? style + extraStyle : extraStyle;
 
            span(end > upto ? text.slice(0, upto - pos) : text, apliedStyle);
 
            var appliedStyle = style;
 
            if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
 
            span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
 
            if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
 
            pos = end;
 
            text = st[i++]; style = st[i++];
 
            text = st[i++]; style = "cm-" + st[i++];
 
          }
 
        }
 
        if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
 
@@ -1716,42 +2091,34 @@ var CodeMirror = (function() {
 
    }
 
  };
 

	
 
  // Event stopping compatibility wrapper.
 
  function stopEvent() {
 
    if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
 
    else {this.returnValue = false; this.cancelBubble = true;}
 
  }
 
  function stopMethod() {e_stop(this);}
 
  // Ensure an event has a stop method.
 
  function addStop(event) {
 
    if (!event.stop) event.stop = stopEvent;
 
    if (!event.stop) event.stop = stopMethod;
 
    return event;
 
  }
 

	
 
  // Event wrapper, exposing the few operations we need.
 
  function Event(orig) {this.e = orig;}
 
  Event.prototype = {
 
    stop: function() {stopEvent.call(this.e);},
 
    target: function() {return this.e.target || this.e.srcElement;},
 
    button: function() {
 
      if (this.e.which) return this.e.which;
 
      else if (this.e.button & 1) return 1;
 
      else if (this.e.button & 2) return 3;
 
      else if (this.e.button & 4) return 2;
 
    },
 
    pageX: function() {
 
      if (this.e.pageX != null) return this.e.pageX;
 
      else return this.e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
 
    },
 
    pageY: function() {
 
      if (this.e.pageY != null) return this.e.pageY;
 
      else return this.e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
 
  function e_preventDefault(e) {
 
    if (e.preventDefault) e.preventDefault();
 
    else e.returnValue = false;
 
  }
 
  function e_stopPropagation(e) {
 
    if (e.stopPropagation) e.stopPropagation();
 
    else e.cancelBubble = true;
 
    }
 
  };
 
  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
 
  function e_target(e) {return e.target || e.srcElement;}
 
  function e_button(e) {
 
    if (e.which) return e.which;
 
    else if (e.button & 1) return 1;
 
    else if (e.button & 2) return 3;
 
    else if (e.button & 4) return 2;
 
  }
 

	
 
  // Event handler registration. If disconnect is true, it'll return a
 
  // function that unregisters the handler.
 
  function connect(node, type, handler, disconnect) {
 
    function wrapHandler(event) {handler(new Event(event || window.event));}
 
    function wrapHandler(event) {handler(event || window.event);}
 
    if (typeof node.addEventListener == "function") {
 
      node.addEventListener(type, wrapHandler, false);
 
      if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
 
@@ -1772,7 +2139,18 @@ var CodeMirror = (function() {
 
    pre.innerHTML = " "; return !pre.innerHTML;
 
  })();
 

	
 
  // Detect drag-and-drop
 
  var dragAndDrop = (function() {
 
    // IE8 has ondragstart and ondrop properties, but doesn't seem to
 
    // actually support ondragstart the way it's supposed to work.
 
    if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
 
    var div = document.createElement('div');
 
    return "ondragstart" in div && "ondrop" in div;
 
  })();
 

	
 
  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
 
  var ie = /MSIE \d/.test(navigator.userAgent);
 
  var safari = /Apple Computer/.test(navigator.vendor);
 

	
 
  var lineSep = "\n";
 
  // Feature-detect whether newlines in textareas are converted to \r\n
 
@@ -1802,11 +2180,23 @@ var CodeMirror = (function() {
 
    return n;
 
  }
 

	
 
  function computedStyle(elt) {
 
    if (elt.currentStyle) return elt.currentStyle;
 
    return window.getComputedStyle(elt, null);
 
  }
 
  // Find the position of an element by following the offsetParent chain.
 
  function eltOffset(node) {
 
    var x = 0, y = 0, n2 = node;
 
    for (var n = node; n; n = n.offsetParent) {x += n.offsetLeft; y += n.offsetTop;}
 
    for (var n = node; n != document.body; n = n.parentNode) {x -= n.scrollLeft; y -= n.scrollTop;}
 
  // If screen==true, it returns screen (rather than page) coordinates.
 
  function eltOffset(node, screen) {
 
    var doc = node.ownerDocument.body;
 
    var x = 0, y = 0, skipDoc = false;
 
    for (var n = node; n; n = n.offsetParent) {
 
      x += n.offsetLeft; y += n.offsetTop;
 
      if (screen && computedStyle(n).position == "fixed")
 
        skipDoc = true;
 
    }
 
    var e = screen && !skipDoc ? null : doc;
 
    for (var n = node.parentNode; n != e; n = n.parentNode)
 
      if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
 
    return {left: x, top: y};
 
  }
 
  // Get a node's text content.
 
@@ -1819,9 +2209,18 @@ var CodeMirror = (function() {
 
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
 
  function copyPos(x) {return {line: x.line, ch: x.ch};}
 

	
 
  var escapeElement = document.createElement("pre");
 
  function htmlEscape(str) {
 
    return str.replace(/[<&]/g, function(str) {return str == "&" ? "&amp;" : "&lt;";});
 
    if (badTextContent) {
 
      escapeElement.innerHTML = "";
 
      escapeElement.appendChild(document.createTextNode(str));
 
    } else {
 
      escapeElement.textContent = str;
 
  }
 
    return escapeElement.innerHTML;
 
  }
 
  var badTextContent = htmlEscape("\t") != "\t";
 
  CodeMirror.htmlEscape = htmlEscape;
 

	
 
  // Used to position the cursor after an undo/redo by finding the
 
  // last edited character.
 
@@ -1842,8 +2241,9 @@ var CodeMirror = (function() {
 

	
 
  // See if "".split is the broken IE version, if so, provide an
 
  // alternative way to split lines.
 
  var splitLines, selRange, setSelRange;
 
  if ("\n\nb".split(/\n/).length != 3)
 
    var splitLines = function(string) {
 
    splitLines = function(string) {
 
      var pos = 0, nl, result = [];
 
      while ((nl = string.indexOf("\n", pos)) > -1) {
 
        result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
 
@@ -1853,23 +2253,40 @@ var CodeMirror = (function() {
 
      return result;
 
    };
 
  else
 
    var splitLines = function(string){return string.split(/\r?\n/);};
 
    splitLines = function(string){return string.split(/\r?\n/);};
 
  CodeMirror.splitLines = splitLines;
 

	
 
  // Sane model of finding and setting the selection in a textarea
 
  if (window.getSelection) {
 
    var selRange = function(te) {
 
    selRange = function(te) {
 
      try {return {start: te.selectionStart, end: te.selectionEnd};}
 
      catch(e) {return null;}
 
    };
 
    var setSelRange = function(te, start, end) {
 
    if (safari)
 
      // On Safari, selection set with setSelectionRange are in a sort
 
      // of limbo wrt their anchor. If you press shift-left in them,
 
      // the anchor is put at the end, and the selection expanded to
 
      // the left. If you press shift-right, the anchor ends up at the
 
      // front. This is not what CodeMirror wants, so it does a
 
      // spurious modify() call to get out of limbo.
 
      setSelRange = function(te, start, end) {
 
        if (start == end)
 
          te.setSelectionRange(start, end);
 
        else {
 
          te.setSelectionRange(start, end - 1);
 
          window.getSelection().modify("extend", "forward", "character");
 
        }
 
      };
 
    else
 
      setSelRange = function(te, start, end) {
 
      try {te.setSelectionRange(start, end);}
 
      catch(e) {} // Fails on Firefox when textarea isn't part of the document
 
    };
 
  }
 
  // IE model. Don't ask.
 
  else {
 
    var selRange = function(te) {
 
      try {var range = document.selection.createRange();}
 
    selRange = function(te) {
 
      try {var range = te.ownerDocument.selection.createRange();}
 
      catch(e) {return null;}
 
      if (!range || range.parentElement() != te) return null;
 
      var val = te.value, len = val.length, localRange = te.createTextRange();
 
@@ -1890,7 +2307,7 @@ var CodeMirror = (function() {
 
      for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
 
      return {start: start, end: end};
 
    };
 
    var setSelRange = function(te, start, end) {
 
    setSelRange = function(te, start, end) {
 
      var range = te.createTextRange();
 
      range.collapse(true);
 
      var endrange = range.duplicate();
0 comments (0 inline, 0 general)