Unreviewed, rolling out r152598.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Jul 2013 06:41:06 +0000 (06:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Jul 2013 06:41:06 +0000 (06:41 +0000)
http://trac.webkit.org/changeset/152598
https://bugs.webkit.org/show_bug.cgi?id=118658

totally broke the web inspector (JS ERROR: SyntaxError:
Expected token '}') (Requested by thorton on #webkit).

* Tools/PrettyPrinting/CodeMirrorFormatters.js:
* Tools/PrettyPrinting/Formatter.js:
* Tools/PrettyPrinting/FormatterContentBuilder.js:
* Tools/PrettyPrinting/codemirror.css:
(.CodeMirror-scrollbar-filler):
(.CodeMirror-gutters):
(.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler):
(.CodeMirror-widget):
* Tools/PrettyPrinting/codemirror.js:
(window.CodeMirror):
(window.CodeMirror.):
(.do):
(moveVisually):
* Tools/PrettyPrinting/css.js:
(.):
(CodeMirror):
* Tools/PrettyPrinting/javascript.js:
(.):
* UserInterface/External/CodeMirror/codemirror.css:
(.CodeMirror-widget):
* UserInterface/External/CodeMirror/codemirror.js:
(window.CodeMirror):
(window.CodeMirror.):
* UserInterface/External/CodeMirror/coffeescript.js:
* UserInterface/External/CodeMirror/comment.js:
(.):
* UserInterface/External/CodeMirror/css.js:
(.):
(CodeMirror):
* UserInterface/External/CodeMirror/javascript.js:
(.):
* UserInterface/External/CodeMirror/matchbrackets.js:
(.):
* UserInterface/External/CodeMirror/sql.js:
* UserInterface/External/CodeMirror/xml.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@152624 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Tools/PrettyPrinting/CodeMirrorFormatters.js
Source/WebInspectorUI/Tools/PrettyPrinting/Formatter.js
Source/WebInspectorUI/Tools/PrettyPrinting/FormatterContentBuilder.js
Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.css
Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.js
Source/WebInspectorUI/Tools/PrettyPrinting/css.js
Source/WebInspectorUI/Tools/PrettyPrinting/javascript.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.css
Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/coffeescript.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/comment.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/css.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/javascript.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/matchbrackets.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/sql.js
Source/WebInspectorUI/UserInterface/External/CodeMirror/xml.js

index 97a88d0..67158c0 100644 (file)
@@ -1,3 +1,48 @@
+2013-07-14  Commit Queue  <commit-queue@webkit.org>
+
+        Unreviewed, rolling out r152598.
+        http://trac.webkit.org/changeset/152598
+        https://bugs.webkit.org/show_bug.cgi?id=118658
+
+        totally broke the web inspector (JS ERROR: SyntaxError:
+        Expected token '}') (Requested by thorton on #webkit).
+
+        * Tools/PrettyPrinting/CodeMirrorFormatters.js:
+        * Tools/PrettyPrinting/Formatter.js:
+        * Tools/PrettyPrinting/FormatterContentBuilder.js:
+        * Tools/PrettyPrinting/codemirror.css:
+        (.CodeMirror-scrollbar-filler):
+        (.CodeMirror-gutters):
+        (.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler):
+        (.CodeMirror-widget):
+        * Tools/PrettyPrinting/codemirror.js:
+        (window.CodeMirror):
+        (window.CodeMirror.):
+        (.do):
+        (moveVisually):
+        * Tools/PrettyPrinting/css.js:
+        (.):
+        (CodeMirror):
+        * Tools/PrettyPrinting/javascript.js:
+        (.):
+        * UserInterface/External/CodeMirror/codemirror.css:
+        (.CodeMirror-widget):
+        * UserInterface/External/CodeMirror/codemirror.js:
+        (window.CodeMirror):
+        (window.CodeMirror.):
+        * UserInterface/External/CodeMirror/coffeescript.js:
+        * UserInterface/External/CodeMirror/comment.js:
+        (.):
+        * UserInterface/External/CodeMirror/css.js:
+        (.):
+        (CodeMirror):
+        * UserInterface/External/CodeMirror/javascript.js:
+        (.):
+        * UserInterface/External/CodeMirror/matchbrackets.js:
+        (.):
+        * UserInterface/External/CodeMirror/sql.js:
+        * UserInterface/External/CodeMirror/xml.js:
+
 2013-07-12  Timothy Hatcher  <timothy@apple.com>
 
         Update CodeMirror to 7f560afa1c (3.14+).
index 81c8155..d5fa941 100644 (file)
@@ -1,28 +1,3 @@
-/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
 // In the inspector token types have been modified to include extra mode information
 // after the actual token type. So we can't do token === "foo". So instead we do
 // /\bfoo\b/.test(token).
@@ -67,8 +42,6 @@ CodeMirror.extendMode("javascript", {
                 return true;
             if (lastContent === "finally")
                 return true;
-            if (lastContent === "do")
-                return true;
             return false;
         }
 
index 524e50d..6fd658e 100644 (file)
@@ -1,28 +1,3 @@
-/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
 function Formatter(codeMirror, builder)
 {
     console.assert(codeMirror);
index 4596880..e94deca 100644 (file)
@@ -1,28 +1,3 @@
-/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
 function FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, originalOffset, formattedOffset, indentString)
 {
     this._originalContent = null;
index 52881f7..0b53704 100644 (file)
@@ -19,7 +19,7 @@
   padding: 0 4px; /* Horizontal padding of content */
 }
 
-.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+.CodeMirror-scrollbar-filler {
   background-color: white; /* The little square between H and V scrollbars */
 }
 
@@ -28,7 +28,6 @@
 .CodeMirror-gutters {
   border-right: 1px solid #ddd;
   background-color: #f7f7f7;
-  white-space: nowrap;
 }
 .CodeMirror-linenumbers {}
 .CodeMirror-linenumber {
@@ -125,7 +124,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 /* The fake, visible scrollbars. Used to force redraw during scrolling
    before actuall scrolling happens, thus preventing shaking and
    flickering artifacts. */
-.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
   position: absolute;
   z-index: 6;
   display: none;
@@ -142,18 +141,16 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 }
 .CodeMirror-scrollbar-filler {
   right: 0; bottom: 0;
-}
-.CodeMirror-gutter-filler {
-  left: 0; bottom: 0;
+  z-index: 6;
 }
 
 .CodeMirror-gutters {
   position: absolute; left: 0; top: 0;
+  height: 100%;
   padding-bottom: 30px;
   z-index: 3;
 }
 .CodeMirror-gutter {
-  white-space: normal;
   height: 100%;
   padding-bottom: 30px;
   margin-bottom: -32px;
@@ -205,6 +202,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 }
 
 .CodeMirror-widget {
+  display: inline-block;
 }
 
 .CodeMirror-wrap .CodeMirror-scroll {
index 9a6a932..9a96cd6 100644 (file)
@@ -97,7 +97,7 @@ window.CodeMirror = (function() {
     else input.setAttribute("wrap", "off");
     // if border: 0; -- iOS fails to open keyboard (issue #1287)
     if (ios) input.style.border = "1px solid black";
-    input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
+    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;");
@@ -105,9 +105,8 @@ window.CodeMirror = (function() {
     d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
     d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
     d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
-    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
     // DIVs containing the selection and the actual code
-    d.lineDiv = elt("div", null, "CodeMirror-code");
+    d.lineDiv = elt("div");
     d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
     // Blinky cursor, and element used to ensure cursor fits at the end of a line
     d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
@@ -127,12 +126,14 @@ window.CodeMirror = (function() {
     // 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.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
     // Provides scrolling
-    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
+    d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
     d.scroller.setAttribute("tabIndex", "-1");
     // The element in which the editor lives.
     d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
-                            d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
+                            d.scrollbarFiller, d.scroller], "CodeMirror");
     // Work around IE7 z-index bug
     if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
     if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
@@ -163,6 +164,8 @@ window.CodeMirror = (function() {
     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 = [];
@@ -211,7 +214,7 @@ window.CodeMirror = (function() {
     estimateLineHeights(cm);
     regChange(cm);
     clearCaches(cm);
-    setTimeout(function(){updateScrollbars(cm);}, 100);
+    setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
   }
 
   function estimateHeight(cm) {
@@ -236,10 +239,9 @@ window.CodeMirror = (function() {
   }
 
   function keyMapChanged(cm) {
-    var map = keyMap[cm.options.keyMap], style = map.style;
+    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 : "");
-    cm.state.disableInput = map.disableInput;
   }
 
   function themeChanged(cm) {
@@ -251,7 +253,6 @@ window.CodeMirror = (function() {
   function guttersChanged(cm) {
     updateGutters(cm);
     regChange(cm);
-    setTimeout(function(){alignHorizontally(cm);}, 20);
   }
 
   function updateGutters(cm) {
@@ -318,14 +319,12 @@ window.CodeMirror = (function() {
 
   // Re-synchronize the fake scrollbars with the actual size of the
   // content. Optionally force a scrollTop.
-  function updateScrollbars(cm) {
-    var d = cm.display, docHeight = cm.doc.height;
+  function updateScrollbars(d /* display */, docHeight) {
     var totalHeight = docHeight + paddingVert(d);
     d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
-    d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
     var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
-    var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
-    var needsV = scrollHeight > (d.scroller.clientHeight + 1);
+    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";
@@ -342,11 +341,6 @@ window.CodeMirror = (function() {
       d.scrollbarFiller.style.display = "block";
       d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
     } else d.scrollbarFiller.style.display = "";
-    if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
-      d.gutterFiller.style.display = "block";
-      d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
-      d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
-    } else d.gutterFiller.style.display = "";
 
     if (mac_geLion && scrollbarWidth(d.measure) === 0)
       d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
@@ -405,10 +399,14 @@ window.CodeMirror = (function() {
     var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
     var visible = visibleLines(cm.display, cm.doc, viewPort);
     for (;;) {
-      if (!updateDisplayInner(cm, changes, visible)) break;
-      updated = true;
+      if (updateDisplayInner(cm, changes, visible)) {
+        updated = true;
+        signalLater(cm, "update", cm);
+        if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
+          signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
+      } else break;
       updateSelection(cm);
-      updateScrollbars(cm);
+      updateScrollbars(cm.display, cm.doc.height);
 
       // Clip forced viewport to actual scrollable area
       if (viewPort)
@@ -420,11 +418,6 @@ window.CodeMirror = (function() {
       changes = [];
     }
 
-    if (updated) {
-      signalLater(cm, "update", cm);
-      if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
-        signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
-    }
     return updated;
   }
 
@@ -512,11 +505,9 @@ window.CodeMirror = (function() {
       display.lastSizeC != display.wrapper.clientHeight;
     // This is just a bogus formula that detects when the editor is
     // resized or the font size changes.
-    if (different) {
-      display.lastSizeC = display.wrapper.clientHeight;
-      startWorker(cm, 400);
-    }
+    if (different) display.lastSizeC = display.wrapper.clientHeight;
     display.showingFrom = from; display.showingTo = to;
+    startWorker(cm, 100);
 
     var prevBottom = display.lineDiv.offsetTop;
     for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
@@ -605,9 +596,8 @@ window.CodeMirror = (function() {
       if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
       if (lineIsHidden(cm.doc, line)) {
         if (line.height != 0) updateLineHeight(line, 0);
-        if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
-          var w = line.widgets[i];
-          if (w.showIfHidden) {
+        if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
+          if (line.widgets[i].showIfHidden) {
             var prev = cur.previousSibling;
             if (/pre/i.test(prev.nodeName)) {
               var wrap = elt("div", null, null, "position: relative");
@@ -615,11 +605,9 @@ window.CodeMirror = (function() {
               wrap.appendChild(prev);
               prev = wrap;
             }
-            var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
-            if (!w.handleMouseEvents) wnode.ignoreEvents = true;
-            positionLineWidget(w, wnode, prev, dims);
+            var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
+            positionLineWidget(line.widgets[i], wnode, prev, dims);
           }
-        }
       } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
         // This line is intact. Skip to the actual node. Update its
         // line number if needed.
@@ -662,25 +650,25 @@ window.CodeMirror = (function() {
 
     if (reuse) {
       reuse.alignable = null;
-      var isOk = true, widgetsSeen = 0, insertBefore = null;
+      var isOk = true, widgetsSeen = 0;
       for (var n = reuse.firstChild, next; n; n = next) {
         next = n.nextSibling;
         if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
           reuse.removeChild(n);
         } else {
-          for (var i = 0; i < line.widgets.length; ++i) {
-            var widget = line.widgets[i];
+          for (var i = 0, first = true; i < line.widgets.length; ++i) {
+            var widget = line.widgets[i], isFirst = false;
+            if (!widget.above) { isFirst = first; first = false; }
             if (widget.node == n.firstChild) {
-              if (!widget.above && !insertBefore) insertBefore = n;
               positionLineWidget(widget, n, reuse, dims);
               ++widgetsSeen;
+              if (isFirst) reuse.insertBefore(lineElement, n);
               break;
             }
           }
           if (i == line.widgets.length) { isOk = false; break; }
         }
       }
-      reuse.insertBefore(lineElement, insertBefore);
       if (isOk && widgetsSeen == line.widgets.length) {
         wrap = reuse;
         reuse.className = line.wrapClass || "";
@@ -715,7 +703,6 @@ window.CodeMirror = (function() {
     if (ie_lt8) wrap.style.zIndex = 2;
     if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
       var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
-      if (!widget.handleMouseEvents) node.ignoreEvents = true;
       positionLineWidget(widget, node, wrap, dims);
       if (widget.above)
         wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
@@ -798,59 +785,67 @@ window.CodeMirror = (function() {
                                "px; height: " + (bottom - top) + "px"));
     }
 
-    function drawForLine(line, fromArg, toArg) {
+    function drawForLine(line, fromArg, toArg, retTop) {
       var lineObj = getLine(doc, line);
-      var lineLen = lineObj.text.length;
-      var start, end;
-      function coords(ch, bias) {
-        return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
+      var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
+      function coords(ch) {
+        return charCoords(cm, Pos(line, ch), "div", lineObj);
       }
 
       iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
-        var leftPos = coords(from, "left"), rightPos, left, right;
-        if (from == to) {
-          rightPos = leftPos;
-          left = right = leftPos.left;
-        } else {
-          rightPos = coords(to - 1, "right");
-          if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
-          left = leftPos.left;
-          right = rightPos.right;
-        }
-        if (fromArg == null && from == 0) left = pl;
+        var leftPos = coords(dir == "rtl" ? to - 1 : from);
+        var rightPos = coords(dir == "rtl" ? from : to - 1);
+        var left = leftPos.left, right = rightPos.right;
         if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
           add(left, leftPos.top, null, leftPos.bottom);
           left = pl;
           if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
         }
         if (toArg == null && to == lineLen) right = clientWidth;
-        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
-          start = leftPos;
-        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
-          end = rightPos;
+        if (fromArg == null && from == 0) left = pl;
+        rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
         if (left < pl + 1) left = pl;
         add(left, rightPos.top, right - left, rightPos.bottom);
       });
-      return {start: start, end: end};
+      return rVal;
     }
 
     if (sel.from.line == sel.to.line) {
       drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
     } else {
-      var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
-      var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
-      var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
-      var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
-      if (singleVLine) {
-        if (leftEnd.top < rightStart.top - 2) {
-          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
-          add(pl, rightStart.top, rightStart.left, rightStart.bottom);
-        } else {
-          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
+      var fromObj = getLine(doc, sel.from.line);
+      var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
+      while (merged = collapsedSpanAtEnd(cur)) {
+        var found = merged.find();
+        path.push(found.from.ch, found.to.line, found.to.ch);
+        if (found.to.line == sel.to.line) {
+          path.push(sel.to.ch);
+          singleLine = true;
+          break;
         }
+        cur = getLine(doc, found.to.line);
+      }
+
+      // This is a single, merged line
+      if (singleLine) {
+        for (var i = 0; i < path.length; i += 3)
+          drawForLine(path[i], path[i+1], path[i+2]);
+      } else {
+        var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
+        if (sel.from.ch)
+          // Draw the first line of selection.
+          middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
+        else
+          // Simply include it in the middle block.
+          middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
+
+        if (!sel.to.ch)
+          middleBot = heightAtLine(cm, toObj) - display.viewOffset;
+        else
+          middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
+
+        if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
       }
-      if (leftEnd.bottom < rightStart.top)
-        add(pl, leftEnd.bottom, null, rightStart.top);
     }
 
     removeChildrenAndAdd(display.selectionDiv, fragment);
@@ -916,12 +911,12 @@ window.CodeMirror = (function() {
   // 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(cm, n, precise) {
+  function findStartLine(cm, n) {
     var minindent, minline, doc = cm.doc;
     for (var search = n, lim = n - 100; search > lim; --search) {
       if (search <= doc.first) return doc.first;
       var line = getLine(doc, search - 1);
-      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
+      if (line.stateAfter) return search;
       var indented = countColumn(line.text, null, cm.options.tabSize);
       if (minline == null || minindent > indented) {
         minline = search - 1;
@@ -931,10 +926,10 @@ window.CodeMirror = (function() {
     return minline;
   }
 
-  function getStateBefore(cm, n, precise) {
+  function getStateBefore(cm, n) {
     var doc = cm.doc, display = cm.display;
       if (!doc.mode.startState) return true;
-    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+    var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
     if (!state) state = startState(doc.mode);
     else state = copyState(doc.mode, state);
     doc.iter(pos, n, function(line) {
@@ -955,7 +950,7 @@ window.CodeMirror = (function() {
     return e.offsetLeft;
   }
 
-  function measureChar(cm, line, ch, data, bias) {
+  function measureChar(cm, line, ch, data) {
     var dir = -1;
     data = data || measureLine(cm, line);
 
@@ -964,13 +959,9 @@ window.CodeMirror = (function() {
       if (r) break;
       if (dir < 0 && pos == 0) dir = 1;
     }
-    bias = pos > ch ? "left" : pos < ch ? "right" : bias;
-    if (bias == "left" && r.leftSide) r = r.leftSide;
-    else if (bias == "right" && r.rightSide) r = r.rightSide;
     return {left: pos < ch ? r.right : r.left,
             right: pos > ch ? r.left : r.right,
-            top: r.top,
-            bottom: r.bottom};
+            top: r.top, bottom: r.bottom};
   }
 
   function findCachedMeasurement(cm, line) {
@@ -980,28 +971,23 @@ window.CodeMirror = (function() {
       if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
           cm.display.scroller.clientWidth == memo.width &&
           memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
-        return memo;
+        return memo.measure;
     }
   }
 
-  function clearCachedMeasurement(cm, line) {
-    var exists = findCachedMeasurement(cm, line);
-    if (exists) exists.text = exists.measure = exists.markedSpans = null;
-  }
-
   function measureLine(cm, line) {
     // First look in the cache
-    var cached = findCachedMeasurement(cm, line);
-    if (cached) return cached.measure;
-
-    // Failing that, recompute and store result in cache
-    var measure = measureLineInner(cm, line);
-    var cache = cm.display.measureLineCache;
-    var memo = {text: line.text, width: cm.display.scroller.clientWidth,
-                markedSpans: line.markedSpans, measure: measure,
-                classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
-    if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
-    else cache.push(memo);
+    var measure = findCachedMeasurement(cm, line);
+    if (!measure) {
+      // Failing that, recompute and store result in cache
+      measure = measureLineInner(cm, line);
+      var cache = cm.display.measureLineCache;
+      var memo = {text: line.text, width: cm.display.scroller.clientWidth,
+                  markedSpans: line.markedSpans, measure: measure,
+                  classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
+      if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
+      else cache.push(memo);
+    }
     return measure;
   }
 
@@ -1043,51 +1029,30 @@ window.CodeMirror = (function() {
     if (ie_lt9 && display.measure.first != pre)
       removeChildrenAndAdd(display.measure, pre);
 
-    function measureRect(rect) {
-      var top = rect.top - outer.top, bot = rect.bottom - outer.top;
-      if (bot > maxBot) bot = maxBot;
-      if (top < 0) top = 0;
-      for (var i = vranges.length - 2; i >= 0; i -= 2) {
-        var rtop = vranges[i], rbot = vranges[i+1];
+    for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
+      var size = getRect(cur);
+      var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
+      for (var j = 0; j < vranges.length; j += 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(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
-          vranges[i] = Math.min(top, rtop);
-          vranges[i+1] = Math.max(bot, rbot);
+          vranges[j] = Math.min(top, rtop);
+          vranges[j+1] = Math.max(bot, rbot);
           break;
         }
       }
-      if (i < 0) { i = vranges.length; vranges.push(top, bot); }
-      return {left: rect.left - outer.left,
-              right: rect.right - outer.left,
-              top: i, bottom: null};
-    }
-    function finishRect(rect) {
-      rect.bottom = vranges[rect.top+1];
-      rect.top = vranges[rect.top];
-    }
-
-    for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
-      var node = cur, rect = null;
-      // A widget might wrap, needs special care
-      if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
-        if (cur.firstChild.nodeType == 1) node = cur.firstChild;
-        var rects = node.getClientRects();
-        if (rects.length > 1) {
-          rect = data[i] = measureRect(rects[0]);
-          rect.rightSide = measureRect(rects[rects.length - 1]);
-        }
-      }
-      if (!rect) rect = data[i] = measureRect(getRect(node));
-      if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
-      if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
+      if (j == vranges.length) vranges.push(top, bot);
+      var right = size.right;
+      if (cur.measureRight) right = getRect(cur.measureRight).left;
+      data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
     }
     for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
-      finishRect(cur);
-      if (cur.leftSide) finishRect(cur.leftSide);
-      if (cur.rightSide) finishRect(cur.rightSide);
+      var vr = cur.top;
+      cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
     }
+
     return data;
   }
 
@@ -1098,7 +1063,7 @@ window.CodeMirror = (function() {
       if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
     }
     var cached = !hasBadSpan && findCachedMeasurement(cm, line);
-    if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
+    if (cached) return measureChar(cm, line, line.text.length, cached).right;
 
     var pre = lineContent(cm, line);
     var end = pre.appendChild(zeroWidthElement(cm.display.measure));
@@ -1113,9 +1078,6 @@ window.CodeMirror = (function() {
     cm.display.lineNumChars = null;
   }
 
-  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
-  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
-
   // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
   function intoCoordSystem(cm, lineObj, rect, context) {
     if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
@@ -1125,12 +1087,11 @@ window.CodeMirror = (function() {
     if (context == "line") return rect;
     if (!context) context = "local";
     var yOff = heightAtLine(cm, lineObj);
-    if (context == "local") yOff += paddingTop(cm.display);
-    else yOff -= cm.display.viewOffset;
-    if (context == "page" || context == "window") {
+    if (context != "local") yOff -= cm.display.viewOffset;
+    if (context == "page") {
       var lOff = getRect(cm.display.lineSpace);
-      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
-      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
+      yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+      var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
       rect.left += xOff; rect.right += xOff;
     }
     rect.top += yOff; rect.bottom += yOff;
@@ -1142,58 +1103,64 @@ window.CodeMirror = (function() {
   function fromCoordSystem(cm, coords, context) {
     if (context == "div") return coords;
     var left = coords.left, top = coords.top;
-    // First move into "page" coordinate system
     if (context == "page") {
-      left -= pageScrollX();
-      top -= pageScrollY();
-    } else if (context == "local" || !context) {
-      var localBox = getRect(cm.display.sizer);
-      left += localBox.left;
-      top += localBox.top;
+      left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
+      top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
     }
-
     var lineSpaceBox = getRect(cm.display.lineSpace);
-    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
+    left -= lineSpaceBox.left;
+    top -= lineSpaceBox.top;
+    if (context == "local" || !context) {
+      var editorBox = getRect(cm.display.wrapper);
+      left += editorBox.left;
+      top += editorBox.top;
+    }
+    return {left: left, top: top};
   }
 
-  function charCoords(cm, pos, context, lineObj, bias) {
+  function charCoords(cm, pos, context, lineObj) {
     if (!lineObj) lineObj = getLine(cm.doc, pos.line);
-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
+    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
   }
 
   function cursorCoords(cm, pos, context, lineObj, measurement) {
     lineObj = lineObj || getLine(cm.doc, pos.line);
     if (!measurement) measurement = measureLine(cm, lineObj);
     function get(ch, right) {
-      var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
+      var m = measureChar(cm, lineObj, ch, measurement);
       if (right) m.left = m.right; else m.right = m.left;
       return intoCoordSystem(cm, lineObj, m, context);
     }
-    function getBidi(ch, partPos) {
-      var part = order[partPos], right = part.level % 2;
-      if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
-        part = order[--partPos];
-        ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
-        right = true;
-      } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
-        part = order[++partPos];
-        ch = bidiLeft(part) - part.level % 2;
-        right = false;
-      }
-      if (right && ch == part.to && ch > part.from) return get(ch - 1);
-      return get(ch, right);
-    }
     var order = getOrder(lineObj), ch = pos.ch;
     if (!order) return get(ch);
-    var partPos = getBidiPartAt(order, ch);
-    var val = getBidi(ch, partPos);
-    if (bidiOther != null) val.other = getBidi(ch, bidiOther);
-    return val;
+    var main, other, linedir = order[0].level;
+    for (var i = 0; i < order.length; ++i) {
+      var part = order[i], rtl = part.level % 2, nb, here;
+      if (part.from < ch && part.to > ch) return get(ch, rtl);
+      var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
+      if (left == ch) {
+        // IE returns 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 (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
+        else here = get(rtl && part.from != part.to ? ch - 1 : ch);
+        if (rtl == linedir) main = here; else other = here;
+      } else if (right == ch) {
+        var nb = i < order.length - 1 && order[i+1];
+        if (!rtl && nb && nb.from == nb.to) continue;
+        if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
+        else here = get(rtl ? ch : ch - 1, true);
+        if (rtl == linedir) main = here; else other = here;
+      }
+    }
+    if (linedir && !ch) other = get(order[0].to - 1);
+    if (!main) return other;
+    if (other) main.other = other;
+    return main;
   }
 
-  function PosWithInfo(line, ch, outside, xRel) {
+  function PosMaybeOutside(line, ch, outside) {
     var pos = new Pos(line, ch);
-    pos.xRel = xRel;
     if (outside) pos.outside = true;
     return pos;
   }
@@ -1202,10 +1169,10 @@ window.CodeMirror = (function() {
   function coordsChar(cm, x, y) {
     var doc = cm.doc;
     y += cm.display.viewOffset;
-    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
+    if (y < 0) return PosMaybeOutside(doc.first, 0, true);
     var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
     if (lineNo > last)
-      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
+      return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
     if (x < 0) x = 0;
 
     for (;;) {
@@ -1213,7 +1180,7 @@ window.CodeMirror = (function() {
       var found = coordsCharInner(cm, lineObj, lineNo, x, y);
       var merged = collapsedSpanAtEnd(lineObj);
       var mergedPos = merged && merged.find();
-      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
+      if (merged && found.ch >= mergedPos.from.ch)
         lineNo = mergedPos.to.line;
       else
         return found;
@@ -1239,15 +1206,14 @@ window.CodeMirror = (function() {
     var from = lineLeft(lineObj), to = lineRight(lineObj);
     var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
 
-    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
+    if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
     // Do a binary search between these bounds.
     for (;;) {
       if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
-        var ch = x < fromX || x - fromX <= toX - x ? from : to;
-        var xDiff = x - (ch == from ? fromX : toX);
+        var after = x - fromX < toX - x, ch = after ? from : to;
         while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
-        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
-                              xDiff < 0 ? -1 : xDiff ? 1 : 0);
+        var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
+        pos.after = after;
         return pos;
       }
       var step = Math.ceil(dist / 2), middle = from + step;
@@ -1256,8 +1222,8 @@ window.CodeMirror = (function() {
         for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
       }
       var middleX = getX(middle);
-      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
-      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
+      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;}
+      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;}
     }
   }
 
@@ -1433,32 +1399,31 @@ window.CodeMirror = (function() {
   // supported or compatible enough yet to rely on.)
   function readInput(cm) {
     var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
-    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
+    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
     var text = input.value;
     if (text == prevInput && posEq(sel.from, sel.to)) return false;
-    if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
+    // IE enjoys randomly deselecting our input's text when
+    // re-focusing. If the selection is gone but the cursor is at the
+    // start of the input, that's probably what happened.
+    if (ie && text && input.selectionStart === 0) {
       resetInput(cm, true);
       return false;
     }
-
     var withOp = !cm.curOp;
     if (withOp) startOperation(cm);
     sel.shift = false;
     var same = 0, l = Math.min(prevInput.length, text.length);
-    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
+    while (same < l && prevInput[same] == text[same]) ++same;
     var from = sel.from, to = sel.to;
     if (same < prevInput.length)
       from = Pos(from.line, from.ch - (prevInput.length - same));
     else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
       to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
-
     var updateInput = cm.curOp.updateInput;
-    var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
-                       origin: cm.state.pasteIncoming ? "paste" : "+input"};
-    makeChange(cm.doc, changeEvent, "end");
-    cm.curOp.updateInput = updateInput;
-    signalLater(cm, "inputRead", cm, changeEvent);
+    makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
+                        origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
 
+    cm.curOp.updateInput = updateInput;
     if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
     else cm.display.prevInput = text;
     if (withOp) endOperation(cm);
@@ -1472,14 +1437,10 @@ window.CodeMirror = (function() {
       cm.display.prevInput = "";
       minimal = hasCopyEvent &&
         (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
-      var content = minimal ? "-" : selected || cm.getSelection();
-      cm.display.input.value = content;
+      if (minimal) cm.display.input.value = "-";
+      else cm.display.input.value = selected || cm.getSelection();
       if (cm.state.focused) selectInput(cm.display.input);
-      if (ie && !ie_lt9) cm.display.inputHasSelection = content;
-    } else if (user) {
-      cm.display.prevInput = cm.display.input.value = "";
-      if (ie && !ie_lt9) cm.display.inputHasSelection = null;
-    }
+    } else if (user) cm.display.prevInput = cm.display.input.value = "";
     cm.display.inaccurateSelection = minimal;
   }
 
@@ -1497,17 +1458,7 @@ window.CodeMirror = (function() {
   function registerEventHandlers(cm) {
     var d = cm.display;
     on(d.scroller, "mousedown", operation(cm, onMouseDown));
-    if (ie)
-      on(d.scroller, "dblclick", operation(cm, function(e) {
-        if (signalDOMEvent(cm, e)) return;
-        var pos = posFromMouse(cm, e);
-        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
-        e_preventDefault(e);
-        var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
-        extendSelection(cm.doc, word.from, word.to);
-      }));
-    else
-      on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
+    on(d.scroller, "dblclick", operation(cm, e_preventDefault));
     on(d.lineSpace, "selectstart", function(e) {
       if (!eventInWidget(d, e)) e_preventDefault(e);
     });
@@ -1539,15 +1490,11 @@ window.CodeMirror = (function() {
     // Prevent wrapper from ever scrolling
     on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
 
-    var resizeTimer;
     function onResize() {
-      if (resizeTimer == null) resizeTimer = setTimeout(function() {
-        resizeTimer = null;
-        // Might be a text scaling operation, clear size caches.
-        d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
-        clearCaches(cm);
-        runInOp(cm, bind(regChange, cm));
-      }, 100);
+      // Might be a text scaling operation, clear size caches.
+      d.cachedCharWidth = d.cachedTextHeight = null;
+      clearCaches(cm);
+      runInOp(cm, bind(regChange, cm));
     }
     on(window, "resize", onResize);
     // Above handler holds on to the editor and its data structures.
@@ -1561,7 +1508,7 @@ window.CodeMirror = (function() {
     setTimeout(unregister, 5000);
 
     on(d.input, "keyup", operation(cm, function(e) {
-      if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+      if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
       if (e.keyCode == 16) cm.doc.sel.shift = false;
     }));
     on(d.input, "input", bind(fastPoll, cm));
@@ -1571,7 +1518,7 @@ window.CodeMirror = (function() {
     on(d.input, "blur", bind(onBlur, cm));
 
     function drag_(e) {
-      if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+      if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
       e_stop(e);
     }
     if (cm.options.dragDrop) {
@@ -1610,7 +1557,9 @@ window.CodeMirror = (function() {
 
   function eventInWidget(display, e) {
     for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
-      if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
+      if (!n) return true;
+      if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
+          n.parentNode == display.sizer && n != display.mover) return true;
     }
   }
 
@@ -1620,7 +1569,7 @@ window.CodeMirror = (function() {
       var target = e_target(e);
       if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
           target == display.scrollbarV || target == display.scrollbarV.firstChild ||
-          target == display.scrollbarFiller || target == display.gutterFiller) return null;
+          target == display.scrollbarFiller) return null;
     }
     var x, y, space = getRect(display.lineSpace);
     // Fails unpredictably on IE[67] when mouse is dragged around quickly.
@@ -1630,7 +1579,6 @@ window.CodeMirror = (function() {
 
   var lastClick, lastDoubleClick;
   function onMouseDown(e) {
-    if (signalDOMEvent(this, e)) return;
     var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
     sel.shift = e.shiftKey;
 
@@ -1701,12 +1649,9 @@ window.CodeMirror = (function() {
     e_preventDefault(e);
     if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
 
-    var startstart = sel.from, startend = sel.to, lastPos = start;
+    var startstart = sel.from, startend = sel.to;
 
     function doSelect(cur) {
-      if (posEq(lastPos, cur)) return;
-      lastPos = cur;
-
       if (type == "single") {
         extendSelection(cm.doc, clipPos(doc, start), cur);
         return;
@@ -1754,6 +1699,8 @@ window.CodeMirror = (function() {
 
     function done(e) {
       counter = Infinity;
+      var cur = posFromMouse(cm, e);
+      if (cur) doSelect(cur);
       e_preventDefault(e);
       focusInput(cm);
       off(document, "mousemove", move);
@@ -1769,41 +1716,11 @@ window.CodeMirror = (function() {
     on(document, "mouseup", up);
   }
 
-  function clickInGutter(cm, e) {
-    var display = cm.display;
-    try { var mX = e.clientX, mY = e.clientY; }
-    catch(e) { return false; }
-
-    if (mX >= Math.floor(getRect(display.gutters).right)) return false;
-    e_preventDefault(e);
-    if (!hasHandler(cm, "gutterClick")) return true;
-
-    var lineBox = getRect(display.lineDiv);
-    if (mY > lineBox.bottom) return true;
-    mY -= lineBox.top - display.viewOffset;
-
-    for (var i = 0; i < cm.options.gutters.length; ++i) {
-      var g = display.gutters.childNodes[i];
-      if (g && getRect(g).right >= mX) {
-        var line = lineAtHeight(cm.doc, mY);
-        var gutter = cm.options.gutters[i];
-        signalLater(cm, "gutterClick", cm, line, gutter, e);
-        break;
-      }
-    }
-    return true;
-  }
-
-  // Kludge to work around strange IE behavior where it'll sometimes
-  // re-fire a series of drag-related events right after the drop (#1551)
-  var lastDrop = 0;
-
   function onDrop(e) {
     var cm = this;
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
+    if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
       return;
     e_preventDefault(e);
-    if (ie) lastDrop = +new Date;
     var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
     if (!pos || isReadOnly(cm)) return;
     if (files && files.length && window.FileReader && window.File) {
@@ -1843,16 +1760,40 @@ window.CodeMirror = (function() {
     }
   }
 
+  function clickInGutter(cm, e) {
+    var display = cm.display;
+    try { var mX = e.clientX, mY = e.clientY; }
+    catch(e) { return false; }
+
+    if (mX >= Math.floor(getRect(display.gutters).right)) return false;
+    e_preventDefault(e);
+    if (!hasHandler(cm, "gutterClick")) return true;
+
+    var lineBox = getRect(display.lineDiv);
+    if (mY > lineBox.bottom) return true;
+    mY -= lineBox.top - display.viewOffset;
+
+    for (var i = 0; i < cm.options.gutters.length; ++i) {
+      var g = display.gutters.childNodes[i];
+      if (g && getRect(g).right >= mX) {
+        var line = lineAtHeight(cm.doc, mY);
+        var gutter = cm.options.gutters[i];
+        signalLater(cm, "gutterClick", cm, line, gutter, e);
+        break;
+      }
+    }
+    return true;
+  }
+
   function onDragStart(cm, e) {
-    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
+    if (eventInWidget(cm.display, e)) return;
 
     var txt = cm.getSelection();
     e.dataTransfer.setData("Text", txt);
 
     // Use dummy image instead of default browsers image.
     // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
+    if (e.dataTransfer.setDragImage) {
       var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
       if (opera) {
         img.width = img.height = 1;
@@ -1860,6 +1801,15 @@ window.CodeMirror = (function() {
         // Force a relayout, or Opera won't use our image for some obscure reason
         img._top = img.offsetTop;
       }
+      if (safari) {
+        if (cm.display.dragImg) {
+          img = cm.display.dragImg;
+        } else {
+          cm.display.dragImg = img;
+          img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+          cm.display.wrapper.appendChild(img);
+        }
+      }
       e.dataTransfer.setDragImage(img, 0, 0);
       if (opera) img.parentNode.removeChild(img);
     }
@@ -1872,7 +1822,6 @@ window.CodeMirror = (function() {
     if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
     if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
     if (gecko) updateDisplay(cm, []);
-    startWorker(cm, 100);
   }
   function setScrollLeft(cm, val, isScroller) {
     if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
@@ -2005,10 +1954,8 @@ window.CodeMirror = (function() {
     var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
     clearTimeout(maybeTransition);
     if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
-      if (getKeyMap(cm.options.keyMap) == startMap) {
+      if (getKeyMap(cm.options.keyMap) == startMap)
         cm.options.keyMap = (next.call ? next.call(null, cm) : next);
-        keyMapChanged(cm);
-      }
     }, 50);
 
     var name = keyName(e, true), handled = false;
@@ -2021,18 +1968,17 @@ window.CodeMirror = (function() {
       // 'go') bound to the keyname without 'Shift-'.
       handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
              || lookupKey(name, keymaps, function(b) {
-                  if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
-                    return doHandleBinding(cm, b);
+                  if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
                 });
     } else {
       handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
     }
+    if (handled == "stop") handled = false;
 
     if (handled) {
       e_preventDefault(e);
       restartBlink(cm);
       if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
-      signalLater(cm, "keyHandled", cm, name, e);
     }
     return handled;
   }
@@ -2043,7 +1989,6 @@ window.CodeMirror = (function() {
     if (handled) {
       e_preventDefault(e);
       restartBlink(cm);
-      signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
     }
     return handled;
   }
@@ -2053,7 +1998,7 @@ window.CodeMirror = (function() {
     var cm = this;
     if (!cm.state.focused) onFocus(cm);
     if (ie && e.keyCode == 27) { e.returnValue = false; }
-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
     var code = e.keyCode;
     // IE does strange things with escape.
     cm.doc.sel.shift = code == 16 || e.shiftKey;
@@ -2069,7 +2014,7 @@ window.CodeMirror = (function() {
 
   function onKeyPress(e) {
     var cm = this;
-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
     var keyCode = e.keyCode, charCode = e.charCode;
     if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
     if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
@@ -2079,7 +2024,6 @@ window.CodeMirror = (function() {
         this.doc.mode.electricChars.indexOf(ch) > -1)
       setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
     if (handleCharBinding(cm, e, ch)) return;
-    if (ie && !ie_lt9) cm.display.inputHasSelection = null;
     fastPoll(cm);
   }
 
@@ -2125,13 +2069,6 @@ window.CodeMirror = (function() {
     // Adds "Select all" to context menu in FF
     if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
 
-    function prepareSelectAllHack() {
-      if (display.input.selectionStart != null) {
-        var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
-        display.prevInput = " ";
-        display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
-      }
-    }
     function rehide() {
       display.inputDiv.style.position = "relative";
       display.input.style.cssText = oldCSS;
@@ -2139,10 +2076,12 @@ window.CodeMirror = (function() {
       slowPoll(cm);
 
       // Try to detect the user choosing select-all
-      if (display.input.selectionStart != null) {
-        if (!ie || ie_lt9) prepareSelectAllHack();
+      if (display.input.selectionStart != null && (!ie || ie_lt9)) {
         clearTimeout(detectingSelectAll);
-        var i = 0, poll = function(){
+        var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
+        display.prevInput = " ";
+        display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
+        var poll = function(){
           if (display.prevInput == " " && display.input.selectionStart == 0)
             operation(cm, commands.selectAll)(cm);
           else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
@@ -2152,7 +2091,6 @@ window.CodeMirror = (function() {
       }
     }
 
-    if (ie && !ie_lt9) prepareSelectAllHack();
     if (captureMiddleClick) {
       e_stop(e);
       var mouseup = function() {
@@ -2167,11 +2105,11 @@ window.CodeMirror = (function() {
 
   // UPDATING
 
-  var changeEnd = CodeMirror.changeEnd = function(change) {
+  function changeEnd(change) {
     if (!change.text) return change.to;
     return Pos(change.from.line + change.text.length - 1,
                lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
-  };
+  }
 
   // Make sure a position will be valid after the given change.
   function clipPostChange(doc, change, pos) {
@@ -2213,21 +2151,21 @@ window.CodeMirror = (function() {
     return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
   }
 
-  function filterChange(doc, change, update) {
+  function filterChange(doc, change) {
     var obj = {
       canceled: false,
       from: change.from,
       to: change.to,
       text: change.text,
       origin: change.origin,
+      update: function(from, to, text, origin) {
+        if (from) this.from = clipPos(doc, from);
+        if (to) this.to = clipPos(doc, to);
+        if (text) this.text = text;
+        if (origin !== undefined) this.origin = origin;
+      },
       cancel: function() { this.canceled = true; }
     };
-    if (update) obj.update = function(from, to, text, origin) {
-      if (from) this.from = clipPos(doc, from);
-      if (to) this.to = clipPos(doc, to);
-      if (text) this.text = text;
-      if (origin !== undefined) this.origin = origin;
-    };
     signal(doc, "beforeChange", doc, obj);
     if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
 
@@ -2244,7 +2182,7 @@ window.CodeMirror = (function() {
     }
 
     if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
-      change = filterChange(doc, change, true);
+      change = filterChange(doc, change);
       if (!change) return;
     }
 
@@ -2283,23 +2221,15 @@ window.CodeMirror = (function() {
     var hist = doc.history;
     var event = (type == "undo" ? hist.done : hist.undone).pop();
     if (!event) return;
+    hist.dirtyCounter += type == "undo" ? -1 : 1;
 
     var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
-                anchorAfter: event.anchorBefore, headAfter: event.headBefore,
-                generation: hist.generation};
+                anchorAfter: event.anchorBefore, headAfter: event.headBefore};
     (type == "undo" ? hist.undone : hist.done).push(anti);
-    hist.generation = event.generation || ++hist.maxGeneration;
-
-    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
 
     for (var i = event.changes.length - 1; i >= 0; --i) {
       var change = event.changes[i];
       change.origin = type;
-      if (filter && !filterChange(doc, change, false)) {
-        (type == "undo" ? hist.done : hist.undone).length = 0;
-        return;
-      }
-
       anti.changes.push(historyChangeFromChange(doc, change));
 
       var after = i ? computeSelAfterChange(doc, change, null)
@@ -2561,11 +2491,11 @@ window.CodeMirror = (function() {
   // SCROLLING
 
   function scrollCursorIntoView(cm) {
-    var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
+    var coords = scrollPosIntoView(cm, cm.doc.sel.head);
     if (!cm.state.focused) return;
-    var display = cm.display, box = getRect(display.sizer), doScroll = null;
-    if (coords.top + box.top < 0) doScroll = true;
-    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+    var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
+    if (coords.top + pTop + box.top < 0) doScroll = true;
+    else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
     if (doScroll != null && !phantom) {
       var hidden = display.cursor.style.display == "none";
       if (hidden) {
@@ -2603,11 +2533,12 @@ window.CodeMirror = (function() {
   }
 
   function calculateScrollPos(cm, x1, y1, x2, y2) {
-    var display = cm.display, snapMargin = textHeight(cm.display);
+    var display = cm.display, pt = paddingTop(display);
+    y1 += pt; y2 += pt;
     if (y1 < 0) y1 = 0;
     var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
     var docBottom = cm.doc.height + paddingVert(display);
-    var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
+    var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
     if (y1 < screentop) {
       result.scrollTop = atTop ? 0 : y1;
     } else if (y2 > screentop + screen) {
@@ -2644,7 +2575,7 @@ window.CodeMirror = (function() {
 
   function indentLine(cm, n, how, aggressive) {
     var doc = cm.doc;
-    if (how == null) how = "add";
+    if (!how) how = "add";
     if (how == "smart") {
       if (!cm.doc.mode.indent) how = "prev";
       else var state = getStateBefore(cm, n);
@@ -2667,8 +2598,6 @@ window.CodeMirror = (function() {
       indentation = curSpace + cm.options.indentUnit;
     } else if (how == "subtract") {
       indentation = curSpace - cm.options.indentUnit;
-    } else if (typeof how == "number") {
-      indentation = curSpace + how;
     }
     indentation = Math.max(0, indentation);
 
@@ -2693,7 +2622,7 @@ window.CodeMirror = (function() {
   }
 
   function findPosH(doc, pos, dir, unit, visually) {
-    var line = pos.line, ch = pos.ch, origDir = dir;
+    var line = pos.line, ch = pos.ch;
     var lineObj = getLine(doc, line);
     var possible = true;
     function findNextLine() {
@@ -2732,7 +2661,7 @@ window.CodeMirror = (function() {
         if (dir > 0 && !moveOnce(!first)) break;
       }
     }
-    var result = skipAtomic(doc, Pos(line, ch), origDir, true);
+    var result = skipAtomic(doc, Pos(line, ch), dir, true);
     if (!possible) result.hitSide = true;
     return result;
   }
@@ -2757,7 +2686,7 @@ window.CodeMirror = (function() {
   function findWordAt(line, pos) {
     var start = pos.ch, end = pos.ch;
     if (line) {
-      if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
+      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);}
@@ -2778,7 +2707,6 @@ window.CodeMirror = (function() {
   // 'wrap f in an operation, performed on its `this` parameter'
 
   CodeMirror.prototype = {
-    constructor: CodeMirror,
     focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
 
     setOption: function(option, value) {
@@ -2798,7 +2726,7 @@ window.CodeMirror = (function() {
     removeKeyMap: function(map) {
       var maps = this.state.keyMaps;
       for (var i = 0; i < maps.length; ++i)
-        if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
+        if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
           maps.splice(i, 1);
           return true;
         }
@@ -2814,8 +2742,7 @@ window.CodeMirror = (function() {
     removeOverlay: operation(null, function(spec) {
       var overlays = this.state.overlays;
       for (var i = 0; i < overlays.length; ++i) {
-        var cur = overlays[i].modeSpec;
-        if (cur == spec || typeof spec == "string" && cur.name == spec) {
+        if (overlays[i].modeSpec == spec) {
           overlays.splice(i, 1);
           this.state.modeGen++;
           regChange(this);
@@ -2825,7 +2752,7 @@ window.CodeMirror = (function() {
     }),
 
     indentLine: operation(null, function(n, dir, aggressive) {
-      if (typeof dir != "string" && typeof dir != "number") {
+      if (typeof dir != "string") {
         if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
         else dir = dir ? "add" : "subtract";
       }
@@ -2840,10 +2767,10 @@ window.CodeMirror = (function() {
 
     // Fetch the parser token for a given character. Useful for hacks
     // that want to inspect the mode state (say, for completion).
-    getTokenAt: function(pos, precise) {
+    getTokenAt: function(pos) {
       var doc = this.doc;
       pos = clipPos(doc, pos);
-      var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
+      var state = getStateBefore(this, pos.line), mode = this.doc.mode;
       var line = getLine(doc, pos.line);
       var stream = new StringStream(line.text, this.options.tabSize);
       while (stream.pos < pos.ch && !stream.eol()) {
@@ -2858,37 +2785,10 @@ window.CodeMirror = (function() {
               state: state};
     },
 
-    getTokenTypeAt: function(pos) {
-      pos = clipPos(this.doc, pos);
-      var styles = getLineStyles(this, getLine(this.doc, pos.line));
-      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
-      if (ch == 0) return styles[2];
-      for (;;) {
-        var mid = (before + after) >> 1;
-        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
-        else if (styles[mid * 2 + 1] < ch) before = mid + 1;
-        else return styles[mid * 2 + 2];
-      }
-    },
-
-    getModeAt: function(pos) {
-      var mode = this.doc.mode;
-      if (!mode.innerMode) return mode;
-      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
-    },
-
-    getHelper: function(pos, type) {
-      if (!helpers.hasOwnProperty(type)) return;
-      var help = helpers[type], mode = this.getModeAt(pos);
-      return mode[type] && help[mode[type]] ||
-        mode.helperType && help[mode.helperType] ||
-        help[mode.name];
-    },
-
-    getStateAfter: function(line, precise) {
+    getStateAfter: function(line) {
       var doc = this.doc;
       line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
-      return getStateBefore(this, line + 1, precise);
+      return getStateBefore(this, line + 1);
     },
 
     cursorCoords: function(start, mode) {
@@ -2908,19 +2808,6 @@ window.CodeMirror = (function() {
       return coordsChar(this, coords.left, coords.top);
     },
 
-    lineAtHeight: function(height, mode) {
-      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
-      return lineAtHeight(this.doc, height + this.display.viewOffset);
-    },
-    heightAtLine: function(line, mode) {
-      var end = false, last = this.doc.first + this.doc.size - 1;
-      if (line < this.doc.first) line = this.doc.first;
-      else if (line > last) { line = last; end = true; }
-      var lineObj = getLine(this.doc, line);
-      return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
-        (end ? lineObj.height : 0);
-    },
-
     defaultTextHeight: function() { return textHeight(this.display); },
     defaultCharWidth: function() { return charWidth(this.display); },
 
@@ -2949,7 +2836,7 @@ window.CodeMirror = (function() {
       return changeLine(this, handle, function(line) {
         var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
         if (!line[prop]) line[prop] = cls;
-        else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
+        else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
         else line[prop] += " " + cls;
         return true;
       });
@@ -2962,10 +2849,9 @@ window.CodeMirror = (function() {
         if (!cur) return false;
         else if (cls == null) line[prop] = null;
         else {
-          var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
-          if (!found) return false;
-          var end = found.index + found[0].length;
-          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
+          var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
+          if (upd == cur) return false;
+          line[prop] = upd || null;
         }
         return true;
       });
@@ -3013,7 +2899,7 @@ window.CodeMirror = (function() {
         if (left + node.offsetWidth > hspace)
           left = hspace - node.offsetWidth;
       }
-      node.style.top = top + "px";
+      node.style.top = (top + paddingTop(display)) + "px";
       node.style.left = node.style.right = "";
       if (horiz == "right") {
         left = display.sizer.clientWidth - node.offsetWidth;
@@ -3081,8 +2967,7 @@ window.CodeMirror = (function() {
       sel.goalColumn = pos.left;
     }),
 
-    toggleOverwrite: function(value) {
-      if (value != null && value == this.state.overwrite) return;
+    toggleOverwrite: function() {
       if (this.state.overwrite = !this.state.overwrite)
         this.display.cursor.className += " CodeMirror-overwrite";
       else
@@ -3123,6 +3008,9 @@ window.CodeMirror = (function() {
       this.refresh();
     },
 
+    on: function(type, f) {on(this, type, f);},
+    off: function(type, f) {off(this, type, f);},
+
     operation: function(f){return runInOp(this, f);},
 
     refresh: operation(null, function() {
@@ -3146,7 +3034,6 @@ window.CodeMirror = (function() {
     getScrollerElement: function(){return this.display.scroller;},
     getGutterElement: function(){return this.display.gutters;}
   };
-  eventMixin(CodeMirror);
 
   // OPTION DEFAULTS
 
@@ -3203,7 +3090,6 @@ window.CodeMirror = (function() {
     cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
     cm.refresh();
   }, true);
-  option("coverGutterNextToScrollbar", false, updateScrollbars, true);
   option("lineNumbers", false, function(cm) {
     setGuttersForLineNumbers(cm.options);
     guttersChanged(cm);
@@ -3219,14 +3105,12 @@ window.CodeMirror = (function() {
   option("dragDrop", true);
 
   option("cursorBlinkRate", 530);
-  option("cursorScrollMargin", 0);
   option("cursorHeight", 1);
   option("workTime", 100);
   option("workDelay", 100);
   option("flattenSpans", true);
   option("pollInterval", 100);
   option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
-  option("historyEventDelay", 500);
   option("viewportMargin", 10, function(cm){cm.refresh();}, true);
   option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
   option("moveInputWithCursor", true, function(cm, val) {
@@ -3257,21 +3141,16 @@ window.CodeMirror = (function() {
   };
 
   CodeMirror.resolveMode = function(spec) {
-    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
       spec = mimeModes[spec];
-    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
-      var found = mimeModes[spec.name];
-      spec = createObj(found, spec);
-      spec.name = found.name;
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+    else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
       return CodeMirror.resolveMode("application/xml");
-    }
     if (typeof spec == "string") return {name: spec};
     else return spec || {name: "null"};
   };
 
   CodeMirror.getMode = function(options, spec) {
-    var spec = CodeMirror.resolveMode(spec);
+    spec = CodeMirror.resolveMode(spec);
     var mfactory = modes[spec.name];
     if (!mfactory) return CodeMirror.getMode(options, "text/plain");
     var modeObj = mfactory(options, spec);
@@ -3284,7 +3163,6 @@ window.CodeMirror = (function() {
       }
     }
     modeObj.name = spec.name;
-
     return modeObj;
   };
 
@@ -3304,20 +3182,12 @@ window.CodeMirror = (function() {
   CodeMirror.defineExtension = function(name, func) {
     CodeMirror.prototype[name] = func;
   };
-  CodeMirror.defineDocExtension = function(name, func) {
-    Doc.prototype[name] = func;
-  };
+
   CodeMirror.defineOption = option;
 
   var initHooks = [];
   CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
 
-  var helpers = CodeMirror.helpers = {};
-  CodeMirror.registerHelper = function(type, name, value) {
-    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
-    helpers[type][name] = value;
-  };
-
   // MODE STATE HANDLING
 
   // Utility functions for working with state. Exported because modes
@@ -3363,10 +3233,6 @@ window.CodeMirror = (function() {
       var l = cm.getCursor().line;
       cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
     },
-    delLineLeft: function(cm) {
-      var cur = cm.getCursor();
-      cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
-    },
     undo: function(cm) {cm.undo();},
     redo: function(cm) {cm.redo();},
     goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
@@ -3462,7 +3328,7 @@ window.CodeMirror = (function() {
     "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
     "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
     "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
-    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
+    "Cmd-[": "indentLess", "Cmd-]": "indentMore",
     fallthrough: ["basic", "emacsy"]
   };
   keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -3501,7 +3367,7 @@ window.CodeMirror = (function() {
 
     for (var i = 0; i < maps.length; ++i) {
       var done = lookup(maps[i]);
-      if (done) return done != "stop";
+      if (done) return done;
     }
   }
   function isModifierKey(event) {
@@ -3655,7 +3521,6 @@ window.CodeMirror = (function() {
     this.doc = doc;
   }
   CodeMirror.TextMarker = TextMarker;
-  eventMixin(TextMarker);
 
   TextMarker.prototype.clear = function() {
     if (this.explicitlyCleared) return;
@@ -3684,7 +3549,7 @@ window.CodeMirror = (function() {
     if (min != null && cm) regChange(cm, min, max + 1);
     this.lines.length = 0;
     this.explicitlyCleared = true;
-    if (this.atomic && this.doc.cantEdit) {
+    if (this.collapsed && this.doc.cantEdit) {
       this.doc.cantEdit = false;
       if (cm) reCheckSelection(cm);
     }
@@ -3707,18 +3572,15 @@ window.CodeMirror = (function() {
     return from && {from: from, to: to};
   };
 
-  TextMarker.prototype.changed = function() {
-    var pos = this.find(), cm = this.doc.cm;
-    if (!pos || !cm) return;
-    var line = getLine(this.doc, pos.from.line);
-    clearCachedMeasurement(cm, line);
-    if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
-      for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
-        if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
-        break;
-      }
-      runInOp(cm, function() { cm.curOp.selectionChanged = true; });
-    }
+  TextMarker.prototype.getOptions = function(copyWidget) {
+    var repl = this.replacedWith;
+    return {className: this.className,
+            inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
+            atomic: this.atomic,
+            collapsed: this.collapsed,
+            replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
+            readOnly: this.readOnly,
+            startStyle: this.startStyle, endStyle: this.endStyle};
   };
 
   TextMarker.prototype.attachLine = function(line) {
@@ -3747,7 +3609,6 @@ window.CodeMirror = (function() {
     if (marker.replacedWith) {
       marker.collapsed = true;
       marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
-      if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
     }
     if (marker.collapsed) sawCollapsedSpans = true;
 
@@ -3790,7 +3651,7 @@ window.CodeMirror = (function() {
     }
     if (cm) {
       if (updateMaxLine) cm.curOp.updateMaxLine = true;
-      if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
+      if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
         regChange(cm, from.line, to.line + 1);
       if (marker.atomic) reCheckSelection(cm);
     }
@@ -3808,7 +3669,6 @@ window.CodeMirror = (function() {
     }
   }
   CodeMirror.SharedTextMarker = SharedTextMarker;
-  eventMixin(SharedTextMarker);
 
   SharedTextMarker.prototype.clear = function() {
     if (this.explicitlyCleared) return;
@@ -3820,6 +3680,11 @@ window.CodeMirror = (function() {
   SharedTextMarker.prototype.find = function() {
     return this.primary.find();
   };
+  SharedTextMarker.prototype.getOptions = function(copyWidget) {
+    var inner = this.primary.getOptions(copyWidget);
+    inner.shared = true;
+    return inner;
+  };
 
   function markTextShared(doc, from, to, options, type) {
     options = copyObj(options);
@@ -3922,13 +3787,6 @@ window.CodeMirror = (function() {
         }
       }
     }
-    if (sameLine && first) {
-      // Make sure we didn't create any zero-length spans
-      for (var i = 0; i < first.length; ++i)
-        if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
-          first.splice(i--, 1);
-      if (!first.length) first = null;
-    }
 
     var newMarkers = [first];
     if (!sameLine) {
@@ -4023,7 +3881,6 @@ window.CodeMirror = (function() {
       sp = sps[i];
       if (!sp.marker.collapsed) continue;
       if (sp.from == null) return true;
-      if (sp.marker.replacedWith) continue;
       if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
         return true;
     }
@@ -4037,7 +3894,7 @@ window.CodeMirror = (function() {
       return true;
     for (var sp, i = 0; i < line.markedSpans.length; ++i) {
       sp = line.markedSpans[i];
-      if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
+      if (sp.marker.collapsed && sp.from == span.to &&
           (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
           lineIsHiddenInner(doc, line, sp)) return true;
     }
@@ -4061,12 +3918,11 @@ window.CodeMirror = (function() {
   // LINE WIDGETS
 
   var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
-    if (options) for (var opt in options) if (options.hasOwnProperty(opt))
+    for (var opt in options) if (options.hasOwnProperty(opt))
       this[opt] = options[opt];
     this.cm = cm;
     this.node = node;
   };
-  eventMixin(LineWidget);
   function widgetOperation(f) {
     return function() {
       var withOp = !this.cm.curOp;
@@ -4081,9 +3937,7 @@ window.CodeMirror = (function() {
     if (no == null || !ws) return;
     for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
     if (!ws.length) this.line.widgets = null;
-    var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop;
     updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
-    if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
     regChange(this.cm, no, no + 1);
   });
   LineWidget.prototype.changed = widgetOperation(function() {
@@ -4107,12 +3961,10 @@ window.CodeMirror = (function() {
     var widget = new LineWidget(cm, node, options);
     if (widget.noHScroll) cm.display.alignWidgets = true;
     changeLine(cm, handle, function(line) {
-      var widgets = line.widgets || (line.widgets = []);
-      if (widget.insertAt == null) widgets.push(widget);
-      else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
+      (line.widgets || (line.widgets = [])).push(widget);
       widget.line = line;
       if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
-        var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop;
+        var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
         updateLineHeight(line, line.height + widgetHeight(widget));
         if (aboveVisible) addToScrollPos(cm, 0, widget.height);
       }
@@ -4125,12 +3977,12 @@ window.CodeMirror = (function() {
 
   // Line objects. These hold state related to a line, including
   // highlighting info (the styles array).
-  var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
-    this.text = text;
-    attachMarkedSpans(this, markedSpans);
-    this.height = estimateHeight ? estimateHeight(this) : 1;
-  };
-  eventMixin(Line);
+  function makeLine(text, markedSpans, estimateHeight) {
+    var line = {text: text};
+    attachMarkedSpans(line, markedSpans);
+    line.height = estimateHeight ? estimateHeight(line) : 1;
+    return line;
+  }
 
   function updateLine(line, text, markedSpans, estimateHeight) {
     line.text = text;
@@ -4154,7 +4006,7 @@ window.CodeMirror = (function() {
   function runMode(cm, text, mode, state, f) {
     var flattenSpans = mode.flattenSpans;
     if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
-    var curStart = 0, curStyle = null;
+    var curText = "", curStyle = null;
     var stream = new StringStream(text, cm.options.tabSize), style;
     if (text == "" && mode.blankLine) mode.blankLine(state);
     while (!stream.eol()) {
@@ -4166,13 +4018,14 @@ window.CodeMirror = (function() {
       } else {
         style = mode.token(stream, state);
       }
-      if (!flattenSpans || curStyle != style) {
-        if (curStart < stream.start) f(stream.start, curStyle);
-        curStart = stream.start; curStyle = style;
-      }
+      var substr = stream.current();
       stream.start = stream.pos;
+      if (!flattenSpans || curStyle != style) {
+        if (curText) f(curText, curStyle);
+        curText = substr; curStyle = style;
+      } else curText = curText + substr;
     }
-    if (curStart < stream.pos) f(stream.pos, curStyle);
+    if (curText) f(curText, curStyle);
   }
 
   function highlightLine(cm, line, state) {
@@ -4180,24 +4033,27 @@ window.CodeMirror = (function() {
     // mode/overlays that it is based on (for easy invalidation).
     var st = [cm.state.modeGen];
     // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
+    runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
 
     // Run overlays, adjust style array.
     for (var o = 0; o < cm.state.overlays.length; ++o) {
-      var overlay = cm.state.overlays[o], i = 1, at = 0;
-      runMode(cm, line.text, overlay.mode, true, function(end, style) {
-        var start = i;
+      var overlay = cm.state.overlays[o], i = 1;
+      runMode(cm, line.text, overlay.mode, true, function(txt, style) {
+        var start = i, len = txt.length;
         // Ensure there's a token end at the current position, and that i points at it
-        while (at < end) {
-          var i_end = st[i];
-          if (i_end > end)
-            st.splice(i, 1, end, st[i+1], i_end);
+        while (len) {
+          var cur = st[i], len_ = cur.length;
+          if (len_ <= len) {
+            len -= len_;
+          } else {
+            st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
+            len = 0;
+          }
           i += 2;
-          at = Math.min(end, i_end);
         }
         if (!style) return;
         if (overlay.opaque) {
-          st.splice(start, i - start, end, style);
+          st.splice(start, i - start, txt, style);
           i = start + 2;
         } else {
           for (; start < i; start += 2) {
@@ -4237,31 +4093,37 @@ window.CodeMirror = (function() {
   }
 
   function lineContent(cm, realLine, measure) {
-    var merged, line = realLine, empty = true;
-    while (merged = collapsedSpanAtStart(line))
+    var merged, line = realLine, lineBefore, sawBefore, simple = true;
+    while (merged = collapsedSpanAtStart(line)) {
+      simple = false;
       line = getLine(cm.doc, merged.find().from.line);
+      if (!lineBefore) lineBefore = line;
+    }
 
     var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
-                   measure: null, measuredSomething: false, cm: cm};
+                   measure: null, addedOne: false, cm: cm};
     if (line.textClass) builder.pre.className = line.textClass;
 
     do {
-      if (line.text) empty = false;
       builder.measure = line == realLine && measure;
       builder.pos = 0;
       builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
       if ((ie || webkit) && cm.getOption("lineWrapping"))
         builder.addToken = buildTokenSplitSpaces(builder.addToken);
-      var next = insertLineContent(line, builder, getLineStyles(cm, line));
-      if (measure && line == realLine && !builder.measuredSomething) {
+      if (measure && sawBefore && line != realLine && !builder.addedOne) {
         measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
-        builder.measuredSomething = true;
+        builder.addedOne = true;
+      }
+      var next = insertLineContent(line, builder, getLineStyles(cm, line));
+      sawBefore = line == lineBefore;
+      if (next) {
+        line = getLine(cm.doc, next.to.line);
+        simple = false;
       }
-      if (next) line = getLine(cm.doc, next.to.line);
     } while (next);
 
-    if (measure && !builder.measuredSomething && !measure[0])
-      measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
+    if (measure && !builder.addedOne)
+      measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
     if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
       builder.pre.appendChild(document.createTextNode("\u00a0"));
 
@@ -4285,7 +4147,7 @@ window.CodeMirror = (function() {
   }
 
   var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
-  function buildToken(builder, text, style, startStyle, endStyle, title) {
+  function buildToken(builder, text, style, startStyle, endStyle) {
     if (!text) return;
     if (!tokenSpecialChars.test(text)) {
       builder.col += text.length;
@@ -4318,9 +4180,7 @@ window.CodeMirror = (function() {
       var fullStyle = style || "";
       if (startStyle) fullStyle += startStyle;
       if (endStyle) fullStyle += endStyle;
-      var token = elt("span", [content], fullStyle);
-      if (title) token.title = title;
-      return builder.pre.appendChild(token);
+      return builder.pre.appendChild(elt("span", [content], fullStyle));
     }
     builder.pre.appendChild(content);
   }
@@ -4332,14 +4192,13 @@ window.CodeMirror = (function() {
       if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
         ch = text.slice(i, i + 2);
         ++i;
-      } else if (i && wrapping && spanAffectsWrapping(text, i)) {
+      } else if (i && wrapping &&
+                 spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
         builder.pre.appendChild(elt("wbr"));
       }
-      var old = builder.measure[builder.pos];
       var span = builder.measure[builder.pos] =
         buildToken(builder, ch, style,
                    start && startStyle, i == text.length - 1 && endStyle);
-      if (old) span.leftSide = old.leftSide || old;
       // In IE single-space nodes wrap differently than spaces
       // embedded in larger text nodes, except when set to
       // white-space: normal (issue #1268).
@@ -4348,7 +4207,7 @@ window.CodeMirror = (function() {
         span.style.whiteSpace = "normal";
       builder.pos += ch.length;
     }
-    if (text.length) builder.measuredSomething = true;
+    if (text.length) builder.addedOne = true;
   }
 
   function buildTokenSplitSpaces(inner) {
@@ -4358,27 +4217,18 @@ window.CodeMirror = (function() {
       out += " ";
       return out;
     }
-    return function(builder, text, style, startStyle, endStyle, title) {
-      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
+    return function(builder, text, style, startStyle, endStyle) {
+      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
     };
   }
 
-  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
-    var widget = !ignoreWidget && marker.replacedWith;
+  function buildCollapsedSpan(builder, size, widget) {
     if (widget) {
       if (!builder.display) widget = widget.cloneNode(true);
       builder.pre.appendChild(widget);
-      if (builder.measure) {
-        if (size) {
-          builder.measure[builder.pos] = widget;
-        } else {
-          var elt = builder.measure[builder.pos] = zeroWidthElement(builder.cm.display.measure);
-          if (marker.type != "bookmark" || marker.insertLeft)
-            builder.pre.insertBefore(elt, widget);
-          else
-            builder.pre.appendChild(elt);
-        }
-        builder.measuredSomething = true;
+      if (builder.measure && size) {
+        builder.measure[builder.pos] = widget;
+        builder.addedOne = true;
       }
     }
     builder.pos += size;
@@ -4387,18 +4237,19 @@ window.CodeMirror = (function() {
   // Outputs a number of spans to make up a line, taking highlighting
   // and marked text into account.
   function insertLineContent(line, builder, styles) {
-    var spans = line.markedSpans, allText = line.text, at = 0;
+    var spans = line.markedSpans;
     if (!spans) {
       for (var i = 1; i < styles.length; i+=2)
-        builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
+        builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
       return;
     }
 
-    var len = allText.length, pos = 0, i = 1, text = "", style;
-    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
+    var allText = line.text, len = allText.length;
+    var pos = 0, i = 1, text = "", style;
+    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
     for (;;) {
       if (nextChange == pos) { // Update current marker set
-        spanStyle = spanEndStyle = spanStartStyle = title = "";
+        spanStyle = spanEndStyle = spanStartStyle = "";
         collapsed = null; nextChange = Infinity;
         var foundBookmark = null;
         for (var j = 0; j < spans.length; ++j) {
@@ -4408,17 +4259,17 @@ window.CodeMirror = (function() {
             if (m.className) spanStyle += " " + m.className;
             if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
             if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
-            if (m.title && !title) title = m.title;
-            if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
+            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;
+          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.marker, collapsed.from == null);
+                             collapsed.from != null && collapsed.marker.replacedWith);
           if (collapsed.to == null) return collapsed.marker.find();
         }
         if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
@@ -4432,14 +4283,13 @@ window.CodeMirror = (function() {
           if (!collapsed) {
             var tokenText = end > upto ? text.slice(0, upto - pos) : text;
             builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
-                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
+                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
           }
           if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
           pos = end;
           spanStartStyle = "";
         }
-        text = allText.slice(at, at = styles[i++]);
-        style = styleToClass(styles[i++]);
+        text = styles[i++]; style = styleToClass(styles[i++]);
       }
     }
   }
@@ -4462,7 +4312,7 @@ window.CodeMirror = (function() {
       // This is a whole-line replace. Treated specially to make
       // sure line objects move the way they are supposed to.
       for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
-        added.push(new Line(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(text[i], spansFor(i), estimateHeight));
       update(lastLine, lastLine.text, lastSpans);
       if (nlines) doc.remove(from.line, nlines);
       if (added.length) doc.insert(from.line, added);
@@ -4471,8 +4321,8 @@ window.CodeMirror = (function() {
         update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
       } else {
         for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
-          added.push(new Line(text[i], spansFor(i), estimateHeight));
-        added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
+          added.push(makeLine(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
         update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
         doc.insert(from.line + 1, added);
       }
@@ -4483,7 +4333,7 @@ window.CodeMirror = (function() {
       update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
       update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
       for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
-        added.push(new Line(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(text[i], spansFor(i), estimateHeight));
       if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
       doc.insert(from.line + 1, added);
     }
@@ -4626,12 +4476,11 @@ window.CodeMirror = (function() {
     if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
     if (firstLine == null) firstLine = 0;
 
-    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
+    BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
     this.first = firstLine;
     this.scrollTop = this.scrollLeft = 0;
     this.cantEdit = false;
     this.history = makeHistory();
-    this.cleanGeneration = 1;
     this.frontier = firstLine;
     var start = Pos(firstLine, 0);
     this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
@@ -4643,7 +4492,6 @@ window.CodeMirror = (function() {
   };
 
   Doc.prototype = createObj(BranchChunk.prototype, {
-    constructor: Doc,
     iter: function(from, to, op) {
       if (op) this.iterN(from - this.first, to - from, op);
       else this.iterN(this.first, this.first + this.size, from);
@@ -4691,11 +4539,6 @@ window.CodeMirror = (function() {
     getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
     getLineNumber: function(line) {return lineNo(line);},
 
-    getLineHandleVisualStart: function(line) {
-      if (typeof line == "number") line = getLine(this, line);
-      return visualLine(this, line);
-    },
-
     lineCount: function() {return this.size;},
     firstLine: function() {return this.first;},
     lastLine: function() {return this.first + this.size - 1;},
@@ -4737,25 +4580,20 @@ window.CodeMirror = (function() {
       var hist = this.history;
       return {undo: hist.done.length, redo: hist.undone.length};
     },
-    clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
+    clearHistory: function() {this.history = makeHistory();},
 
     markClean: function() {
-      this.cleanGeneration = this.changeGeneration();
-    },
-    changeGeneration: function() {
+      this.history.dirtyCounter = 0;
       this.history.lastOp = this.history.lastOrigin = null;
-      return this.history.generation;
-    },
-    isClean: function (gen) {
-      return this.history.generation == (gen || this.cleanGeneration);
     },
+    isClean: function () {return this.history.dirtyCounter == 0;},
 
     getHistory: function() {
       return {done: copyHistoryArray(this.history.done),
               undone: copyHistoryArray(this.history.undone)};
     },
     setHistory: function(histData) {
-      var hist = this.history = makeHistory(this.history.maxGeneration);
+      var hist = this.history = makeHistory();
       hist.done = histData.done.slice(0);
       hist.undone = histData.undone.slice(0);
     },
@@ -4866,8 +4704,6 @@ window.CodeMirror = (function() {
       return function() {return method.apply(this.doc, arguments);};
     })(Doc.prototype[prop]);
 
-  eventMixin(Doc);
-
   function linkedDocs(doc, f, sharedHistOnly) {
     function propagate(doc, skip, sharedHist) {
       if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
@@ -4987,7 +4823,7 @@ window.CodeMirror = (function() {
 
   // HISTORY
 
-  function makeHistory(startGen) {
+  function makeHistory() {
     return {
       // Arrays of history events. Doing something adds an event to
       // done and clears undo. Undoing moves events from done to
@@ -4997,7 +4833,7 @@ window.CodeMirror = (function() {
       // event
       lastTime: 0, lastOp: null, lastOrigin: null,
       // Used by the isClean() method
-      generation: startGen || 1, maxGeneration: startGen || 1
+      dirtyCounter: 0
     };
   }
 
@@ -5011,8 +4847,7 @@ window.CodeMirror = (function() {
   }
 
   function historyChangeFromChange(doc, change) {
-    var from = { line: change.from.line, ch: change.from.ch };
-    var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+    var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
     attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
     linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
     return histChange;
@@ -5026,8 +4861,7 @@ window.CodeMirror = (function() {
     if (cur &&
         (hist.lastOp == opId ||
          hist.lastOrigin == change.origin && change.origin &&
-         ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
-          change.origin.charAt(0) == "*"))) {
+         ((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) {
       // Merge this change into the last event
       var last = lst(cur.changes);
       if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
@@ -5042,13 +4876,17 @@ window.CodeMirror = (function() {
     } else {
       // Can not be merged, start a new event.
       cur = {changes: [historyChangeFromChange(doc, change)],
-             generation: hist.generation,
              anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
              anchorAfter: selAfter.anchor, headAfter: selAfter.head};
       hist.done.push(cur);
-      hist.generation = ++hist.maxGeneration;
       while (hist.done.length > hist.undoDepth)
         hist.done.shift();
+      if (hist.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.
+        hist.dirtyCounter = NaN;
+      else
+        hist.dirtyCounter++;
     }
     hist.lastTime = time;
     hist.lastOp = opId;
@@ -5163,9 +5001,6 @@ window.CodeMirror = (function() {
     if (e.stopPropagation) e.stopPropagation();
     else e.cancelBubble = true;
   }
-  function e_defaultPrevented(e) {
-    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
-  }
   function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
   CodeMirror.e_stop = e_stop;
   CodeMirror.e_preventDefault = e_preventDefault;
@@ -5232,11 +5067,6 @@ window.CodeMirror = (function() {
       delayedCallbacks.push(bnd(arr[i]));
   }
 
-  function signalDOMEvent(cm, e) {
-    signal(cm, e.type, cm, e);
-    return e_defaultPrevented(e);
-  }
-
   function fireDelayed() {
     --delayedCallbackDepth;
     var delayed = delayedCallbacks;
@@ -5251,11 +5081,6 @@ window.CodeMirror = (function() {
 
   CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
 
-  function eventMixin(ctor) {
-    ctor.prototype.on = function(type, f) {on(this, type, f);};
-    ctor.prototype.off = function(type, f) {off(this, type, f);};
-  }
-
   // MISC UTILITIES
 
   // Number of pixels added to scroller and sizer to hide scrollbar
@@ -5296,11 +5121,7 @@ window.CodeMirror = (function() {
     if (ios) { // Mobile Safari apparently has a bug where select() is broken.
       node.selectionStart = 0;
       node.selectionEnd = node.value.length;
-    } else {
-      // Suppress mysterious IE10 errors
-      try { node.select(); }
-      catch(_e) {}
-    }
+    } else node.select();
   }
 
   function indexOf(collection, elt) {
@@ -5334,7 +5155,7 @@ window.CodeMirror = (function() {
     return function(){return f.apply(null, args);};
   }
 
-  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
   function isWordChar(ch) {
     return /\w/.test(ch) || ch > "\x80" &&
       (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
@@ -5395,26 +5216,13 @@ window.CodeMirror = (function() {
   // word wrapping between certain characters *only* if a new inline
   // element is started between them. This makes it hard to reliably
   // measure the position of things, since that requires inserting an
-  // extra span. This terribly fragile set of tests matches the
+  // extra span. This terribly fragile set of regexps matches the
   // character combinations that suffer from this phenomenon on the
   // various browsers.
-  function spanAffectsWrapping() { return false; }
-  if (gecko) // Only for "$'"
-    spanAffectsWrapping = function(str, i) {
-      return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
-    };
-  else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
-    spanAffectsWrapping = function(str, i) {
-      return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
-    };
-  else if (webkit && !/Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
-    spanAffectsWrapping = function(str, i) {
-      if (i > 1 && str.charCodeAt(i - 1) == 45) {
-        if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
-        if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
-      }
-      return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
-    };
+  var spanAffectsWrapping = /^$/; // Won't match any two-character string
+  if (gecko) spanAffectsWrapping = /$'/;
+  else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
+  else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
 
   var knownScrollbarWidth;
   function scrollbarWidth(measure) {
@@ -5499,15 +5307,11 @@ window.CodeMirror = (function() {
 
   function iterateBidiSections(order, from, to, f) {
     if (!order) return f(from, to, "ltr");
-    var found = false;
     for (var i = 0; i < order.length; ++i) {
       var part = order[i];
-      if (part.from < to && part.to > from || from == to && part.to == from) {
+      if (part.from < to && part.to > from || from == to && part.to == from)
         f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
-        found = true;
-      }
     }
-    if (!found) f(from, to, "ltr");
   }
 
   function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
@@ -5537,40 +5341,6 @@ window.CodeMirror = (function() {
     return Pos(lineN, ch);
   }
 
-  function compareBidiLevel(order, a, b) {
-    var linedir = order[0].level;
-    if (a == linedir) return true;
-    if (b == linedir) return false;
-    return a < b;
-  }
-  var bidiOther;
-  function getBidiPartAt(order, pos) {
-    for (var i = 0, found; i < order.length; ++i) {
-      var cur = order[i];
-      if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
-      if (cur.from == pos || cur.to == pos) {
-        if (found == null) {
-          found = i;
-        } else if (compareBidiLevel(order, cur.level, order[found].level)) {
-          bidiOther = found;
-          return i;
-        } else {
-          bidiOther = i;
-          return found;
-        }
-      }
-    }
-    bidiOther = null;
-    return found;
-  }
-
-  function moveInLine(line, pos, dir, byUnit) {
-    if (!byUnit) return pos + dir;
-    do pos += dir;
-    while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
-    return pos;
-  }
-
   // This is somewhat involved. It is needed in order to move
   // 'visually' through bi-directional text -- i.e., pressing left
   // should make the cursor go left, even when in RTL text. The
@@ -5580,24 +5350,37 @@ window.CodeMirror = (function() {
   function moveVisually(line, start, dir, byUnit) {
     var bidi = getOrder(line);
     if (!bidi) return moveLogically(line, start, dir, byUnit);
-    var pos = getBidiPartAt(bidi, start), part = bidi[pos];
-    var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
-
-    for (;;) {
-      if (target > part.from && target < part.to) return target;
-      if (target == part.from || target == part.to) {
-        if (getBidiPartAt(bidi, target) == pos) return target;
-        part = bidi[pos += dir];
-        return (dir > 0) == part.level % 2 ? part.to : part.from;
+    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 {
-        part = bidi[pos += dir];
-        if (!part) return null;
-        if ((dir > 0) == part.level % 2)
-          target = moveInLine(line, part.to, -1, byUnit);
-        else
-          target = moveInLine(line, part.from, 1, byUnit);
+        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) {
@@ -5769,7 +5552,7 @@ window.CodeMirror = (function() {
 
   // THE END
 
-  CodeMirror.version = "3.14.1";
+  CodeMirror.version = "3.11 +";
 
   return CodeMirror;
 })();
index b52e787..1ef72b5 100644 (file)
@@ -103,8 +103,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
     startState: function(base) {
       return {tokenize: tokenBase,
               baseIndent: base || 0,
-              stack: [],
-              lastToken: null};
+              stack: []};
     },
 
     token: function(stream, state) {
@@ -164,29 +163,28 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
       var context = state.stack[state.stack.length-1];
       if (style == "variable") {
         if (type == "variable-definition") state.stack.push("propertyValue");
-        return state.lastToken = "variable-2";
+        return "variable-2";
       } else if (style == "property") {
-        var word = stream.current().toLowerCase();
-        if (context == "propertyValue") {
-          if (valueKeywords.hasOwnProperty(word)) {
+        if (context == "propertyValue"){
+          if (valueKeywords[stream.current()]) {
             style = "string-2";
-          } else if (colorKeywords.hasOwnProperty(word)) {
+          } else if (colorKeywords[stream.current()]) {
             style = "keyword";
           } else {
             style = "variable-2";
           }
         } else if (context == "rule") {
-          if (!propertyKeywords.hasOwnProperty(word)) {
+          if (!propertyKeywords[stream.current()]) {
             style += " error";
           }
         } else if (context == "block") {
           // if a value is present in both property, value, or color, the order
           // of preference is property -> color -> value
-          if (propertyKeywords.hasOwnProperty(word)) {
+          if (propertyKeywords[stream.current()]) {
             style = "property";
-          } else if (colorKeywords.hasOwnProperty(word)) {
+          } else if (colorKeywords[stream.current()]) {
             style = "keyword";
-          } else if (valueKeywords.hasOwnProperty(word)) {
+          } else if (valueKeywords[stream.current()]) {
             style = "string-2";
           } else {
             style = "tag";
@@ -196,42 +194,42 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
         } else if (context == "@media") {
           if (atMediaTypes[stream.current()]) {
             style = "attribute"; // Known attribute
-          } else if (/^(only|not)$/.test(word)) {
+          } else if (/^(only|not)$/i.test(stream.current())) {
             style = "keyword";
-          } else if (word == "and") {
+          } else if (stream.current().toLowerCase() == "and") {
             style = "error"; // "and" is only allowed in @mediaType
-          } else if (atMediaFeatures.hasOwnProperty(word)) {
+          } else if (atMediaFeatures[stream.current()]) {
             style = "error"; // Known property, should be in @mediaType(
           } else {
             // Unknown, expecting keyword or attribute, assuming attribute
             style = "attribute error";
           }
         } else if (context == "@mediaType") {
-          if (atMediaTypes.hasOwnProperty(word)) {
+          if (atMediaTypes[stream.current()]) {
             style = "attribute";
-          } else if (word == "and") {
+          } else if (stream.current().toLowerCase() == "and") {
             style = "operator";
-          } else if (/^(only|not)$/.test(word)) {
+          } else if (/^(only|not)$/i.test(stream.current())) {
             style = "error"; // Only allowed in @media
+          } else if (atMediaFeatures[stream.current()]) {
+            style = "error"; // Known property, should be in parentheses
           } else {
             // Unknown attribute or property, but expecting property (preceded
             // by "and"). Should be in parentheses
             style = "error";
           }
         } else if (context == "@mediaType(") {
-          if (propertyKeywords.hasOwnProperty(word)) {
+          if (propertyKeywords[stream.current()]) {
             // do nothing, remains "property"
-          } else if (atMediaTypes.hasOwnProperty(word)) {
+          } else if (atMediaTypes[stream.current()]) {
             style = "error"; // Known property, should be in parentheses
-          } else if (word == "and") {
+          } else if (stream.current().toLowerCase() == "and") {
             style = "operator";
-          } else if (/^(only|not)$/.test(word)) {
+          } else if (/^(only|not)$/i.test(stream.current())) {
             style = "error"; // Only allowed in @media
           } else {
             style += " error";
           }
-        } else if (context == "@import") {
-          style = "tag";
         } else {
           style = "error";
         }
@@ -268,16 +266,14 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
       }
       else if (type == "interpolation") state.stack.push("interpolation");
       else if (type == "@media") state.stack.push("@media");
-      else if (type == "@import") state.stack.push("@import");
       else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
         state.stack.push("@mediaType");
       else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
       else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
       else if (context == "@mediaType(" && type == ")") state.stack.pop();
-      else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
+      else if ((context == "rule" || context == "block") && type == ":") state.stack.push("propertyValue");
       else if (context == "propertyValue" && type == ";") state.stack.pop();
-      else if (context == "@import" && type == ";") state.stack.pop();
-      return state.lastToken = style;
+      return style;
     },
 
     indent: function(state, textAfter) {
@@ -287,10 +283,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
       return state.baseIndent + n * indentUnit;
     },
 
-    electricChars: "}",
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    fold: "brace"
+    electricChars: "}"
   };
 });
 
@@ -394,46 +387,12 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
     "vertical-align", "visibility", "voice-balance", "voice-duration",
     "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
     "voice-volume", "volume", "white-space", "widows", "width", "word-break",
-    "word-spacing", "word-wrap", "z-index",
-    // SVG-specific
-    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
-    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
-    "color-interpolation", "color-interpolation-filters", "color-profile",
-    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
-    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
-    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
-    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
-    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
-    "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
+    "word-spacing", "word-wrap", "z-index"
   ]);
 
   var colorKeywords = keySet([
-    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
-    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
-    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
-    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
-    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
-    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
-    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
-    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
-    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
-    "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew",
-    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
-    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
-    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
-    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
-    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
-    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
-    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
-    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
-    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
-    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
-    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
-    "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
-    "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
-    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
-    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
-    "whitesmoke", "yellow", "yellowgreen"
+    "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
+    "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
   ]);
 
   var valueKeywords = keySet([
@@ -516,7 +475,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
     "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
     "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
     "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
-    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
+    "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
     "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
     "xx-large", "xx-small"
   ]);
index 3d04603..229fb2d 100644 (file)
@@ -2,7 +2,6 @@
 
 CodeMirror.defineMode("javascript", function(config, parserConfig) {
   var indentUnit = config.indentUnit;
-  var statementIndent = parserConfig.statementIndent;
   var jsonMode = parserConfig.json;
   var isTS = parserConfig.typescript;
 
@@ -14,7 +13,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     var operator = kw("operator"), atom = {type: "atom", style: "atom"};
 
     var jsKeywords = {
-      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+      "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
       "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
       "var": kw("var"), "const": kw("var"), "let": kw("var"),
       "function": kw("function"), "catch": kw("catch"),
@@ -227,9 +226,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   }
   function pushlex(type, info) {
     var result = function() {
-      var state = cx.state, indent = state.indented;
-      if (state.lexical.type == "stat") indent = state.lexical.indented;
-      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
+      var state = cx.state;
+      state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
     };
     result.lex = true;
     return result;
@@ -258,32 +256,30 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
     if (type == "{") return cont(pushlex("}"), block, poplex);
     if (type == ";") return cont();
-    if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
     if (type == "function") return cont(functiondef);
     if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
-                                   poplex, statement, poplex);
+                                      poplex, statement, poplex);
     if (type == "variable") return cont(pushlex("stat"), maybelabel);
     if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
-                                      block, poplex, poplex);
+                                         block, poplex, poplex);
     if (type == "case") return cont(expression, expect(":"));
     if (type == "default") return cont(expect(":"));
     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
-                                     statement, poplex, popcontext);
+                                        statement, poplex, popcontext);
     return pass(pushlex("stat"), expression, expect(";"), poplex);
   }
   function expression(type) {
-    return expressionInner(type, false);
+    return expressionInner(type, maybeoperatorComma);
   }
   function expressionNoComma(type) {
-    return expressionInner(type, true);
+    return expressionInner(type, maybeoperatorNoComma);
   }
-  function expressionInner(type, noComma) {
-    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+  function expressionInner(type, maybeop) {
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (type == "function") return cont(functiondef);
-    if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+    if (type == "keyword c") return cont(maybeexpression);
     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
-    if (type == "operator") return cont(noComma ? expressionNoComma : expression);
+    if (type == "operator") return cont(expression);
     if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
     if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
     return cont();
@@ -292,10 +288,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type.match(/[;\}\)\],]/)) return pass();
     return pass(expression);
   }
-  function maybeexpressionNoComma(type) {
-    if (type.match(/[;\}\)\],]/)) return pass();
-    return pass(expressionNoComma);
-  }
 
   function maybeoperatorComma(type, value) {
     if (type == ",") return cont(expression);
@@ -311,7 +303,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == ";") return;
     if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
     if (type == ".") return cont(property, me);
-    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+    if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, me);
   }
   function maybelabel(type) {
     if (type == ":") return cont(poplex, statement);
@@ -373,9 +365,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (value == "=") return cont(expressionNoComma, vardef2);
     if (type == ",") return cont(vardef1);
   }
-  function maybeelse(type, value) {
-    if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
-  }
   function forspec1(type) {
     if (type == "var") return cont(vardef1, expect(";"), forspec2);
     if (type == ";") return cont(forspec2);
@@ -427,7 +416,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       if (state.tokenize != jsTokenComment && stream.eatSpace()) return null;
       var style = state.tokenize(stream, state);
       if (type == "comment") return style;
-      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
+      state.lastType = type;
       return parseJS(state, style, type, content, stream);
     },
 
@@ -435,35 +424,26 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
       if (state.tokenize != jsTokenBase) return 0;
       var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
-      // Kludge to prevent 'maybelse' from blocking lexical scope pops
-      for (var i = state.cc.length - 1; i >= 0; --i) {
-        var c = state.cc[i];
-        if (c == poplex) lexical = lexical.prev;
-        else if (c != maybeelse || /^else\b/.test(textAfter)) break;
-      }
       if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
-      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
-        lexical = lexical.prev;
       var type = lexical.type, closing = firstChar == type;
+      if (parserConfig.statementIndent != null) {
+        if (type == ")" && lexical.prev && lexical.prev.type == "stat") lexical = lexical.prev;
+        if (lexical.type == "stat") return lexical.indented + parserConfig.statementIndent;
+      }
 
       if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "stat")
-        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
-      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
+        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
+      else if (lexical.info == "switch" && !closing)
         return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
       else if (lexical.align) return lexical.column + (closing ? 0 : 1);
       else return lexical.indented + (closing ? 0 : indentUnit);
     },
 
     electricChars: ":{}",
-    blockCommentStart: jsonMode ? null : "/*",
-    blockCommentEnd: jsonMode ? null : "*/",
-    lineComment: jsonMode ? null : "//",
-    fold: "brace",
 
-    helperType: jsonMode ? "json" : "javascript",
     jsonMode: jsonMode
   };
 });
@@ -473,6 +453,5 @@ CodeMirror.defineMIME("text/ecmascript", "javascript");
 CodeMirror.defineMIME("application/javascript", "javascript");
 CodeMirror.defineMIME("application/ecmascript", "javascript");
 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
-CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
index 52881f7..f5379d9 100644 (file)
@@ -205,6 +205,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 }
 
 .CodeMirror-widget {
+  display: inline-block;
 }
 
 .CodeMirror-wrap .CodeMirror-scroll {
index 9a6a932..3a591b2 100644 (file)
@@ -236,10 +236,9 @@ window.CodeMirror = (function() {
   }
 
   function keyMapChanged(cm) {
-    var map = keyMap[cm.options.keyMap], style = map.style;
+    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 : "");
-    cm.state.disableInput = map.disableInput;
   }
 
   function themeChanged(cm) {
@@ -324,8 +323,8 @@ window.CodeMirror = (function() {
     d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
     d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
     var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
-    var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
-    var needsV = scrollHeight > (d.scroller.clientHeight + 1);
+    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";
@@ -512,11 +511,9 @@ window.CodeMirror = (function() {
       display.lastSizeC != display.wrapper.clientHeight;
     // This is just a bogus formula that detects when the editor is
     // resized or the font size changes.
-    if (different) {
-      display.lastSizeC = display.wrapper.clientHeight;
-      startWorker(cm, 400);
-    }
+    if (different) display.lastSizeC = display.wrapper.clientHeight;
     display.showingFrom = from; display.showingTo = to;
+    startWorker(cm, 100);
 
     var prevBottom = display.lineDiv.offsetTop;
     for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
@@ -605,9 +602,8 @@ window.CodeMirror = (function() {
       if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
       if (lineIsHidden(cm.doc, line)) {
         if (line.height != 0) updateLineHeight(line, 0);
-        if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
-          var w = line.widgets[i];
-          if (w.showIfHidden) {
+        if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
+          if (line.widgets[i].showIfHidden) {
             var prev = cur.previousSibling;
             if (/pre/i.test(prev.nodeName)) {
               var wrap = elt("div", null, null, "position: relative");
@@ -615,11 +611,9 @@ window.CodeMirror = (function() {
               wrap.appendChild(prev);
               prev = wrap;
             }
-            var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
-            if (!w.handleMouseEvents) wnode.ignoreEvents = true;
-            positionLineWidget(w, wnode, prev, dims);
+            var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
+            positionLineWidget(line.widgets[i], wnode, prev, dims);
           }
-        }
       } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
         // This line is intact. Skip to the actual node. Update its
         // line number if needed.
@@ -662,25 +656,25 @@ window.CodeMirror = (function() {
 
     if (reuse) {
       reuse.alignable = null;
-      var isOk = true, widgetsSeen = 0, insertBefore = null;
+      var isOk = true, widgetsSeen = 0;
       for (var n = reuse.firstChild, next; n; n = next) {
         next = n.nextSibling;
         if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
           reuse.removeChild(n);
         } else {
-          for (var i = 0; i < line.widgets.length; ++i) {
-            var widget = line.widgets[i];
+          for (var i = 0, first = true; i < line.widgets.length; ++i) {
+            var widget = line.widgets[i], isFirst = false;
+            if (!widget.above) { isFirst = first; first = false; }
             if (widget.node == n.firstChild) {
-              if (!widget.above && !insertBefore) insertBefore = n;
               positionLineWidget(widget, n, reuse, dims);
               ++widgetsSeen;
+              if (isFirst) reuse.insertBefore(lineElement, n);
               break;
             }
           }
           if (i == line.widgets.length) { isOk = false; break; }
         }
       }
-      reuse.insertBefore(lineElement, insertBefore);
       if (isOk && widgetsSeen == line.widgets.length) {
         wrap = reuse;
         reuse.className = line.wrapClass || "";
@@ -715,7 +709,6 @@ window.CodeMirror = (function() {
     if (ie_lt8) wrap.style.zIndex = 2;
     if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
       var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
-      if (!widget.handleMouseEvents) node.ignoreEvents = true;
       positionLineWidget(widget, node, wrap, dims);
       if (widget.above)
         wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
@@ -798,59 +791,74 @@ window.CodeMirror = (function() {
                                "px; height: " + (bottom - top) + "px"));
     }
 
-    function drawForLine(line, fromArg, toArg) {
+    function drawForLine(line, fromArg, toArg, retTop) {
       var lineObj = getLine(doc, line);
-      var lineLen = lineObj.text.length;
-      var start, end;
-      function coords(ch, bias) {
-        return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
+      var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
+      function coords(ch) {
+        return charCoords(cm, Pos(line, ch), "div", lineObj);
       }
 
       iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
-        var leftPos = coords(from, "left"), rightPos, left, right;
+        var leftPos = coords(from), rightPos, left, right;
         if (from == to) {
           rightPos = leftPos;
           left = right = leftPos.left;
         } else {
-          rightPos = coords(to - 1, "right");
+          rightPos = coords(to - 1);
           if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
           left = leftPos.left;
           right = rightPos.right;
         }
-        if (fromArg == null && from == 0) left = pl;
         if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
           add(left, leftPos.top, null, leftPos.bottom);
           left = pl;
           if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
         }
         if (toArg == null && to == lineLen) right = clientWidth;
-        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
-          start = leftPos;
-        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
-          end = rightPos;
+        if (fromArg == null && from == 0) left = pl;
+        rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
         if (left < pl + 1) left = pl;
         add(left, rightPos.top, right - left, rightPos.bottom);
       });
-      return {start: start, end: end};
+      return rVal;
     }
 
     if (sel.from.line == sel.to.line) {
       drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
     } else {
-      var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
-      var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
-      var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
-      var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
-      if (singleVLine) {
-        if (leftEnd.top < rightStart.top - 2) {
-          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
-          add(pl, rightStart.top, rightStart.left, rightStart.bottom);
-        } else {
-          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
+      var fromObj = getLine(doc, sel.from.line);
+      var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
+      while (merged = collapsedSpanAtEnd(cur)) {
+        var found = merged.find();
+        path.push(found.from.ch, found.to.line, found.to.ch);
+        if (found.to.line == sel.to.line) {
+          path.push(sel.to.ch);
+          singleLine = true;
+          break;
         }
+        cur = getLine(doc, found.to.line);
+      }
+
+      // This is a single, merged line
+      if (singleLine) {
+        for (var i = 0; i < path.length; i += 3)
+          drawForLine(path[i], path[i+1], path[i+2]);
+      } else {
+        var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
+        if (sel.from.ch)
+          // Draw the first line of selection.
+          middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
+        else
+          // Simply include it in the middle block.
+          middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
+
+        if (!sel.to.ch)
+          middleBot = heightAtLine(cm, toObj) - display.viewOffset;
+        else
+          middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
+
+        if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
       }
-      if (leftEnd.bottom < rightStart.top)
-        add(pl, leftEnd.bottom, null, rightStart.top);
     }
 
     removeChildrenAndAdd(display.selectionDiv, fragment);
@@ -916,12 +924,12 @@ window.CodeMirror = (function() {
   // 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(cm, n, precise) {
+  function findStartLine(cm, n) {
     var minindent, minline, doc = cm.doc;
     for (var search = n, lim = n - 100; search > lim; --search) {
       if (search <= doc.first) return doc.first;
       var line = getLine(doc, search - 1);
-      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
+      if (line.stateAfter) return search;
       var indented = countColumn(line.text, null, cm.options.tabSize);
       if (minline == null || minindent > indented) {
         minline = search - 1;
@@ -931,10 +939,10 @@ window.CodeMirror = (function() {
     return minline;
   }
 
-  function getStateBefore(cm, n, precise) {
+  function getStateBefore(cm, n) {
     var doc = cm.doc, display = cm.display;
       if (!doc.mode.startState) return true;
-    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+    var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
     if (!state) state = startState(doc.mode);
     else state = copyState(doc.mode, state);
     doc.iter(pos, n, function(line) {
@@ -955,7 +963,7 @@ window.CodeMirror = (function() {
     return e.offsetLeft;
   }
 
-  function measureChar(cm, line, ch, data, bias) {
+  function measureChar(cm, line, ch, data) {
     var dir = -1;
     data = data || measureLine(cm, line);
 
@@ -964,13 +972,9 @@ window.CodeMirror = (function() {
       if (r) break;
       if (dir < 0 && pos == 0) dir = 1;
     }
-    bias = pos > ch ? "left" : pos < ch ? "right" : bias;
-    if (bias == "left" && r.leftSide) r = r.leftSide;
-    else if (bias == "right" && r.rightSide) r = r.rightSide;
     return {left: pos < ch ? r.right : r.left,
             right: pos > ch ? r.left : r.right,
-            top: r.top,
-            bottom: r.bottom};
+            top: r.top, bottom: r.bottom};
   }
 
   function findCachedMeasurement(cm, line) {
@@ -1043,51 +1047,30 @@ window.CodeMirror = (function() {
     if (ie_lt9 && display.measure.first != pre)
       removeChildrenAndAdd(display.measure, pre);
 
-    function measureRect(rect) {
-      var top = rect.top - outer.top, bot = rect.bottom - outer.top;
-      if (bot > maxBot) bot = maxBot;
-      if (top < 0) top = 0;
-      for (var i = vranges.length - 2; i >= 0; i -= 2) {
-        var rtop = vranges[i], rbot = vranges[i+1];
+    for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
+      var size = getRect(cur);
+      var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
+      for (var j = 0; j < vranges.length; j += 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(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
-          vranges[i] = Math.min(top, rtop);
-          vranges[i+1] = Math.max(bot, rbot);
+          vranges[j] = Math.min(top, rtop);
+          vranges[j+1] = Math.max(bot, rbot);
           break;
         }
       }
-      if (i < 0) { i = vranges.length; vranges.push(top, bot); }
-      return {left: rect.left - outer.left,
-              right: rect.right - outer.left,
-              top: i, bottom: null};
-    }
-    function finishRect(rect) {
-      rect.bottom = vranges[rect.top+1];
-      rect.top = vranges[rect.top];
-    }
-
-    for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
-      var node = cur, rect = null;
-      // A widget might wrap, needs special care
-      if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
-        if (cur.firstChild.nodeType == 1) node = cur.firstChild;
-        var rects = node.getClientRects();
-        if (rects.length > 1) {
-          rect = data[i] = measureRect(rects[0]);
-          rect.rightSide = measureRect(rects[rects.length - 1]);
-        }
-      }
-      if (!rect) rect = data[i] = measureRect(getRect(node));
-      if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
-      if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
+      if (j == vranges.length) vranges.push(top, bot);
+      var right = size.right;
+      if (cur.measureRight) right = getRect(cur.measureRight).left;
+      data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
     }
     for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
-      finishRect(cur);
-      if (cur.leftSide) finishRect(cur.leftSide);
-      if (cur.rightSide) finishRect(cur.rightSide);
+      var vr = cur.top;
+      cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
     }
+
     return data;
   }
 
@@ -1098,7 +1081,7 @@ window.CodeMirror = (function() {
       if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
     }
     var cached = !hasBadSpan && findCachedMeasurement(cm, line);
-    if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
+    if (cached) return measureChar(cm, line, line.text.length, cached.measure).right;
 
     var pre = lineContent(cm, line);
     var end = pre.appendChild(zeroWidthElement(cm.display.measure));
@@ -1113,9 +1096,6 @@ window.CodeMirror = (function() {
     cm.display.lineNumChars = null;
   }
 
-  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
-  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
-
   // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
   function intoCoordSystem(cm, lineObj, rect, context) {
     if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
@@ -1125,12 +1105,11 @@ window.CodeMirror = (function() {
     if (context == "line") return rect;
     if (!context) context = "local";
     var yOff = heightAtLine(cm, lineObj);
-    if (context == "local") yOff += paddingTop(cm.display);
-    else yOff -= cm.display.viewOffset;
-    if (context == "page" || context == "window") {
+    if (context != "local") yOff -= cm.display.viewOffset;
+    if (context == "page") {
       var lOff = getRect(cm.display.lineSpace);
-      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
-      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
+      yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+      var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
       rect.left += xOff; rect.right += xOff;
     }
     rect.top += yOff; rect.bottom += yOff;
@@ -1142,30 +1121,31 @@ window.CodeMirror = (function() {
   function fromCoordSystem(cm, coords, context) {
     if (context == "div") return coords;
     var left = coords.left, top = coords.top;
-    // First move into "page" coordinate system
     if (context == "page") {
-      left -= pageScrollX();
-      top -= pageScrollY();
-    } else if (context == "local" || !context) {
-      var localBox = getRect(cm.display.sizer);
-      left += localBox.left;
-      top += localBox.top;
+      left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
+      top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
     }
-
     var lineSpaceBox = getRect(cm.display.lineSpace);
-    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
+    left -= lineSpaceBox.left;
+    top -= lineSpaceBox.top;
+    if (context == "local" || !context) {
+      var editorBox = getRect(cm.display.wrapper);
+      left += editorBox.left;
+      top += editorBox.top;
+    }
+    return {left: left, top: top};
   }
 
-  function charCoords(cm, pos, context, lineObj, bias) {
+  function charCoords(cm, pos, context, lineObj) {
     if (!lineObj) lineObj = getLine(cm.doc, pos.line);
-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
+    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
   }
 
   function cursorCoords(cm, pos, context, lineObj, measurement) {
     lineObj = lineObj || getLine(cm.doc, pos.line);
     if (!measurement) measurement = measureLine(cm, lineObj);
     function get(ch, right) {
-      var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
+      var m = measureChar(cm, lineObj, ch, measurement);
       if (right) m.left = m.right; else m.right = m.left;
       return intoCoordSystem(cm, lineObj, m, context);
     }
@@ -1191,9 +1171,8 @@ window.CodeMirror = (function() {
     return val;
   }
 
-  function PosWithInfo(line, ch, outside, xRel) {
+  function PosMaybeOutside(line, ch, outside) {
     var pos = new Pos(line, ch);
-    pos.xRel = xRel;
     if (outside) pos.outside = true;
     return pos;
   }
@@ -1202,10 +1181,10 @@ window.CodeMirror = (function() {
   function coordsChar(cm, x, y) {
     var doc = cm.doc;
     y += cm.display.viewOffset;
-    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
+    if (y < 0) return PosMaybeOutside(doc.first, 0, true);
     var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
     if (lineNo > last)
-      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
+      return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
     if (x < 0) x = 0;
 
     for (;;) {
@@ -1213,7 +1192,7 @@ window.CodeMirror = (function() {
       var found = coordsCharInner(cm, lineObj, lineNo, x, y);
       var merged = collapsedSpanAtEnd(lineObj);
       var mergedPos = merged && merged.find();
-      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
+      if (merged && found.ch >= mergedPos.from.ch)
         lineNo = mergedPos.to.line;
       else
         return found;
@@ -1239,15 +1218,14 @@ window.CodeMirror = (function() {
     var from = lineLeft(lineObj), to = lineRight(lineObj);
     var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
 
-    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
+    if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
     // Do a binary search between these bounds.
     for (;;) {
       if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
-        var ch = x < fromX || x - fromX <= toX - x ? from : to;
-        var xDiff = x - (ch == from ? fromX : toX);
+        var after = x - fromX < toX - x, ch = after ? from : to;
         while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
-        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
-                              xDiff < 0 ? -1 : xDiff ? 1 : 0);
+        var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
+        pos.after = after;
         return pos;
       }
       var step = Math.ceil(dist / 2), middle = from + step;
@@ -1433,7 +1411,7 @@ window.CodeMirror = (function() {
   // supported or compatible enough yet to rely on.)
   function readInput(cm) {
     var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
-    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
+    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
     var text = input.value;
     if (text == prevInput && posEq(sel.from, sel.to)) return false;
     if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
@@ -1451,14 +1429,11 @@ window.CodeMirror = (function() {
       from = Pos(from.line, from.ch - (prevInput.length - same));
     else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
       to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
-
     var updateInput = cm.curOp.updateInput;
-    var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
-                       origin: cm.state.pasteIncoming ? "paste" : "+input"};
-    makeChange(cm.doc, changeEvent, "end");
-    cm.curOp.updateInput = updateInput;
-    signalLater(cm, "inputRead", cm, changeEvent);
+    makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
+                        origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
 
+    cm.curOp.updateInput = updateInput;
     if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
     else cm.display.prevInput = text;
     if (withOp) endOperation(cm);
@@ -1499,7 +1474,6 @@ window.CodeMirror = (function() {
     on(d.scroller, "mousedown", operation(cm, onMouseDown));
     if (ie)
       on(d.scroller, "dblclick", operation(cm, function(e) {
-        if (signalDOMEvent(cm, e)) return;
         var pos = posFromMouse(cm, e);
         if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
         e_preventDefault(e);
@@ -1507,7 +1481,7 @@ window.CodeMirror = (function() {
         extendSelection(cm.doc, word.from, word.to);
       }));
     else
-      on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
+      on(d.scroller, "dblclick", e_preventDefault);
     on(d.lineSpace, "selectstart", function(e) {
       if (!eventInWidget(d, e)) e_preventDefault(e);
     });
@@ -1539,15 +1513,11 @@ window.CodeMirror = (function() {
     // Prevent wrapper from ever scrolling
     on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
 
-    var resizeTimer;
     function onResize() {
-      if (resizeTimer == null) resizeTimer = setTimeout(function() {
-        resizeTimer = null;
-        // Might be a text scaling operation, clear size caches.
-        d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
-        clearCaches(cm);
-        runInOp(cm, bind(regChange, cm));
-      }, 100);
+      // Might be a text scaling operation, clear size caches.
+      d.cachedCharWidth = d.cachedTextHeight = null;
+      clearCaches(cm);
+      runInOp(cm, bind(regChange, cm));
     }
     on(window, "resize", onResize);
     // Above handler holds on to the editor and its data structures.
@@ -1561,7 +1531,7 @@ window.CodeMirror = (function() {
     setTimeout(unregister, 5000);
 
     on(d.input, "keyup", operation(cm, function(e) {
-      if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+      if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
       if (e.keyCode == 16) cm.doc.sel.shift = false;
     }));
     on(d.input, "input", bind(fastPoll, cm));
@@ -1571,7 +1541,7 @@ window.CodeMirror = (function() {
     on(d.input, "blur", bind(onBlur, cm));
 
     function drag_(e) {
-      if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+      if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
       e_stop(e);
     }
     if (cm.options.dragDrop) {
@@ -1610,7 +1580,9 @@ window.CodeMirror = (function() {
 
   function eventInWidget(display, e) {
     for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
-      if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
+      if (!n) return true;
+      if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
+          n.parentNode == display.sizer && n != display.mover) return true;
     }
   }
 
@@ -1630,7 +1602,6 @@ window.CodeMirror = (function() {
 
   var lastClick, lastDoubleClick;
   function onMouseDown(e) {
-    if (signalDOMEvent(this, e)) return;
     var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
     sel.shift = e.shiftKey;
 
@@ -1754,6 +1725,8 @@ window.CodeMirror = (function() {
 
     function done(e) {
       counter = Infinity;
+      var cur = posFromMouse(cm, e);
+      if (cur) doSelect(cur);
       e_preventDefault(e);
       focusInput(cm);
       off(document, "mousemove", move);
@@ -1769,41 +1742,11 @@ window.CodeMirror = (function() {
     on(document, "mouseup", up);
   }
 
-  function clickInGutter(cm, e) {
-    var display = cm.display;
-    try { var mX = e.clientX, mY = e.clientY; }
-    catch(e) { return false; }
-
-    if (mX >= Math.floor(getRect(display.gutters).right)) return false;
-    e_preventDefault(e);
-    if (!hasHandler(cm, "gutterClick")) return true;
-
-    var lineBox = getRect(display.lineDiv);
-    if (mY > lineBox.bottom) return true;
-    mY -= lineBox.top - display.viewOffset;
-
-    for (var i = 0; i < cm.options.gutters.length; ++i) {
-      var g = display.gutters.childNodes[i];
-      if (g && getRect(g).right >= mX) {
-        var line = lineAtHeight(cm.doc, mY);
-        var gutter = cm.options.gutters[i];
-        signalLater(cm, "gutterClick", cm, line, gutter, e);
-        break;
-      }
-    }
-    return true;
-  }
-
-  // Kludge to work around strange IE behavior where it'll sometimes
-  // re-fire a series of drag-related events right after the drop (#1551)
-  var lastDrop = 0;
-
   function onDrop(e) {
     var cm = this;
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
+    if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
       return;
     e_preventDefault(e);
-    if (ie) lastDrop = +new Date;
     var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
     if (!pos || isReadOnly(cm)) return;
     if (files && files.length && window.FileReader && window.File) {
@@ -1843,16 +1786,41 @@ window.CodeMirror = (function() {
     }
   }
 
+  function clickInGutter(cm, e) {
+    var display = cm.display;
+    try { var mX = e.clientX, mY = e.clientY; }
+    catch(e) { return false; }
+
+    if (mX >= Math.floor(getRect(display.gutters).right)) return false;
+    e_preventDefault(e);
+    if (!hasHandler(cm, "gutterClick")) return true;
+
+    var lineBox = getRect(display.lineDiv);
+    if (mY > lineBox.bottom) return true;
+    mY -= lineBox.top - display.viewOffset;
+
+    for (var i = 0; i < cm.options.gutters.length; ++i) {
+      var g = display.gutters.childNodes[i];
+      if (g && getRect(g).right >= mX) {
+        var line = lineAtHeight(cm.doc, mY);
+        var gutter = cm.options.gutters[i];
+        signalLater(cm, "gutterClick", cm, line, gutter, e);
+        break;
+      }
+    }
+    return true;
+  }
+
   function onDragStart(cm, e) {
-    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
+    if (ie && !cm.state.draggingText) { e_stop(e); return; }
+    if (eventInWidget(cm.display, e)) return;
 
     var txt = cm.getSelection();
     e.dataTransfer.setData("Text", txt);
 
     // Use dummy image instead of default browsers image.
     // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
+    if (e.dataTransfer.setDragImage) {
       var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
       if (opera) {
         img.width = img.height = 1;
@@ -1860,6 +1828,15 @@ window.CodeMirror = (function() {
         // Force a relayout, or Opera won't use our image for some obscure reason
         img._top = img.offsetTop;
       }
+      if (safari) {
+        if (cm.display.dragImg) {
+          img = cm.display.dragImg;
+        } else {
+          cm.display.dragImg = img;
+          img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+          cm.display.wrapper.appendChild(img);
+        }
+      }
       e.dataTransfer.setDragImage(img, 0, 0);
       if (opera) img.parentNode.removeChild(img);
     }
@@ -1872,7 +1849,6 @@ window.CodeMirror = (function() {
     if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
     if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
     if (gecko) updateDisplay(cm, []);
-    startWorker(cm, 100);
   }
   function setScrollLeft(cm, val, isScroller) {
     if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
@@ -2005,10 +1981,8 @@ window.CodeMirror = (function() {
     var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
     clearTimeout(maybeTransition);
     if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
-      if (getKeyMap(cm.options.keyMap) == startMap) {
+      if (getKeyMap(cm.options.keyMap) == startMap)
         cm.options.keyMap = (next.call ? next.call(null, cm) : next);
-        keyMapChanged(cm);
-      }
     }, 50);
 
     var name = keyName(e, true), handled = false;
@@ -2021,18 +1995,17 @@ window.CodeMirror = (function() {
       // 'go') bound to the keyname without 'Shift-'.
       handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
              || lookupKey(name, keymaps, function(b) {
-                  if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
-                    return doHandleBinding(cm, b);
+                  if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
                 });
     } else {
       handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
     }
+    if (handled == "stop") handled = false;
 
     if (handled) {
       e_preventDefault(e);
       restartBlink(cm);
       if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
-      signalLater(cm, "keyHandled", cm, name, e);
     }
     return handled;
   }
@@ -2043,7 +2016,6 @@ window.CodeMirror = (function() {
     if (handled) {
       e_preventDefault(e);
       restartBlink(cm);
-      signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
     }
     return handled;
   }
@@ -2053,7 +2025,7 @@ window.CodeMirror = (function() {
     var cm = this;
     if (!cm.state.focused) onFocus(cm);
     if (ie && e.keyCode == 27) { e.returnValue = false; }
-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
     var code = e.keyCode;
     // IE does strange things with escape.
     cm.doc.sel.shift = code == 16 || e.shiftKey;
@@ -2069,7 +2041,7 @@ window.CodeMirror = (function() {
 
   function onKeyPress(e) {
     var cm = this;
-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
     var keyCode = e.keyCode, charCode = e.charCode;
     if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
     if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
@@ -2167,11 +2139,11 @@ window.CodeMirror = (function() {
 
   // UPDATING
 
-  var changeEnd = CodeMirror.changeEnd = function(change) {
+  function changeEnd(change) {
     if (!change.text) return change.to;
     return Pos(change.from.line + change.text.length - 1,
                lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
-  };
+  }
 
   // Make sure a position will be valid after the given change.
   function clipPostChange(doc, change, pos) {
@@ -2213,21 +2185,21 @@ window.CodeMirror = (function() {
     return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
   }
 
-  function filterChange(doc, change, update) {
+  function filterChange(doc, change) {
     var obj = {
       canceled: false,
       from: change.from,
       to: change.to,
       text: change.text,
       origin: change.origin,
+      update: function(from, to, text, origin) {
+        if (from) this.from = clipPos(doc, from);
+        if (to) this.to = clipPos(doc, to);
+        if (text) this.text = text;
+        if (origin !== undefined) this.origin = origin;
+      },
       cancel: function() { this.canceled = true; }
     };
-    if (update) obj.update = function(from, to, text, origin) {
-      if (from) this.from = clipPos(doc, from);
-      if (to) this.to = clipPos(doc, to);
-      if (text) this.text = text;
-      if (origin !== undefined) this.origin = origin;
-    };
     signal(doc, "beforeChange", doc, obj);
     if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
 
@@ -2244,7 +2216,7 @@ window.CodeMirror = (function() {
     }
 
     if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
-      change = filterChange(doc, change, true);
+      change = filterChange(doc, change);
       if (!change) return;
     }
 
@@ -2283,23 +2255,15 @@ window.CodeMirror = (function() {
     var hist = doc.history;
     var event = (type == "undo" ? hist.done : hist.undone).pop();
     if (!event) return;
+    hist.dirtyCounter += type == "undo" ? -1 : 1;
 
     var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
-                anchorAfter: event.anchorBefore, headAfter: event.headBefore,
-                generation: hist.generation};
+                anchorAfter: event.anchorBefore, headAfter: event.headBefore};
     (type == "undo" ? hist.undone : hist.done).push(anti);
-    hist.generation = event.generation || ++hist.maxGeneration;
-
-    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
 
     for (var i = event.changes.length - 1; i >= 0; --i) {
       var change = event.changes[i];
       change.origin = type;
-      if (filter && !filterChange(doc, change, false)) {
-        (type == "undo" ? hist.done : hist.undone).length = 0;
-        return;
-      }
-
       anti.changes.push(historyChangeFromChange(doc, change));
 
       var after = i ? computeSelAfterChange(doc, change, null)
@@ -2563,9 +2527,9 @@ window.CodeMirror = (function() {
   function scrollCursorIntoView(cm) {
     var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
     if (!cm.state.focused) return;
-    var display = cm.display, box = getRect(display.sizer), doScroll = null;
-    if (coords.top + box.top < 0) doScroll = true;
-    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+    var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
+    if (coords.top + pTop + box.top < 0) doScroll = true;
+    else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
     if (doScroll != null && !phantom) {
       var hidden = display.cursor.style.display == "none";
       if (hidden) {
@@ -2603,11 +2567,12 @@ window.CodeMirror = (function() {
   }
 
   function calculateScrollPos(cm, x1, y1, x2, y2) {
-    var display = cm.display, snapMargin = textHeight(cm.display);
+    var display = cm.display, pt = paddingTop(display);
+    y1 += pt; y2 += pt;
     if (y1 < 0) y1 = 0;
     var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
     var docBottom = cm.doc.height + paddingVert(display);
-    var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
+    var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
     if (y1 < screentop) {
       result.scrollTop = atTop ? 0 : y1;
     } else if (y2 > screentop + screen) {
@@ -2644,7 +2609,7 @@ window.CodeMirror = (function() {
 
   function indentLine(cm, n, how, aggressive) {
     var doc = cm.doc;
-    if (how == null) how = "add";
+    if (!how) how = "add";
     if (how == "smart") {
       if (!cm.doc.mode.indent) how = "prev";
       else var state = getStateBefore(cm, n);
@@ -2667,8 +2632,6 @@ window.CodeMirror = (function() {
       indentation = curSpace + cm.options.indentUnit;
     } else if (how == "subtract") {
       indentation = curSpace - cm.options.indentUnit;
-    } else if (typeof how == "number") {
-      indentation = curSpace + how;
     }
     indentation = Math.max(0, indentation);
 
@@ -2757,7 +2720,7 @@ window.CodeMirror = (function() {
   function findWordAt(line, pos) {
     var start = pos.ch, end = pos.ch;
     if (line) {
-      if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
+      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);}
@@ -2798,7 +2761,7 @@ window.CodeMirror = (function() {
     removeKeyMap: function(map) {
       var maps = this.state.keyMaps;
       for (var i = 0; i < maps.length; ++i)
-        if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
+        if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
           maps.splice(i, 1);
           return true;
         }
@@ -2814,8 +2777,7 @@ window.CodeMirror = (function() {
     removeOverlay: operation(null, function(spec) {
       var overlays = this.state.overlays;
       for (var i = 0; i < overlays.length; ++i) {
-        var cur = overlays[i].modeSpec;
-        if (cur == spec || typeof spec == "string" && cur.name == spec) {
+        if (overlays[i].modeSpec == spec) {
           overlays.splice(i, 1);
           this.state.modeGen++;
           regChange(this);
@@ -2825,7 +2787,7 @@ window.CodeMirror = (function() {
     }),
 
     indentLine: operation(null, function(n, dir, aggressive) {
-      if (typeof dir != "string" && typeof dir != "number") {
+      if (typeof dir != "string") {
         if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
         else dir = dir ? "add" : "subtract";
       }
@@ -2840,10 +2802,10 @@ window.CodeMirror = (function() {
 
     // Fetch the parser token for a given character. Useful for hacks
     // that want to inspect the mode state (say, for completion).
-    getTokenAt: function(pos, precise) {
+    getTokenAt: function(pos) {
       var doc = this.doc;
       pos = clipPos(doc, pos);
-      var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
+      var state = getStateBefore(this, pos.line), mode = this.doc.mode;
       var line = getLine(doc, pos.line);
       var stream = new StringStream(line.text, this.options.tabSize);
       while (stream.pos < pos.ch && !stream.eol()) {
@@ -2858,37 +2820,10 @@ window.CodeMirror = (function() {
               state: state};
     },
 
-    getTokenTypeAt: function(pos) {
-      pos = clipPos(this.doc, pos);
-      var styles = getLineStyles(this, getLine(this.doc, pos.line));
-      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
-      if (ch == 0) return styles[2];
-      for (;;) {
-        var mid = (before + after) >> 1;
-        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
-        else if (styles[mid * 2 + 1] < ch) before = mid + 1;
-        else return styles[mid * 2 + 2];
-      }
-    },
-
-    getModeAt: function(pos) {
-      var mode = this.doc.mode;
-      if (!mode.innerMode) return mode;
-      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
-    },
-
-    getHelper: function(pos, type) {
-      if (!helpers.hasOwnProperty(type)) return;
-      var help = helpers[type], mode = this.getModeAt(pos);
-      return mode[type] && help[mode[type]] ||
-        mode.helperType && help[mode.helperType] ||
-        help[mode.name];
-    },
-
-    getStateAfter: function(line, precise) {
+    getStateAfter: function(line) {
       var doc = this.doc;
       line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
-      return getStateBefore(this, line + 1, precise);
+      return getStateBefore(this, line + 1);
     },
 
     cursorCoords: function(start, mode) {
@@ -2908,19 +2843,6 @@ window.CodeMirror = (function() {
       return coordsChar(this, coords.left, coords.top);
     },
 
-    lineAtHeight: function(height, mode) {
-      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
-      return lineAtHeight(this.doc, height + this.display.viewOffset);
-    },
-    heightAtLine: function(line, mode) {
-      var end = false, last = this.doc.first + this.doc.size - 1;
-      if (line < this.doc.first) line = this.doc.first;
-      else if (line > last) { line = last; end = true; }
-      var lineObj = getLine(this.doc, line);
-      return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
-        (end ? lineObj.height : 0);
-    },
-
     defaultTextHeight: function() { return textHeight(this.display); },
     defaultCharWidth: function() { return charWidth(this.display); },
 
@@ -2949,7 +2871,7 @@ window.CodeMirror = (function() {
       return changeLine(this, handle, function(line) {
         var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
         if (!line[prop]) line[prop] = cls;
-        else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
+        else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
         else line[prop] += " " + cls;
         return true;
       });
@@ -2962,10 +2884,9 @@ window.CodeMirror = (function() {
         if (!cur) return false;
         else if (cls == null) line[prop] = null;
         else {
-          var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
-          if (!found) return false;
-          var end = found.index + found[0].length;
-          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
+          var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
+          if (upd == cur) return false;
+          line[prop] = upd || null;
         }
         return true;
       });
@@ -3013,7 +2934,7 @@ window.CodeMirror = (function() {
         if (left + node.offsetWidth > hspace)
           left = hspace - node.offsetWidth;
       }
-      node.style.top = top + "px";
+      node.style.top = (top + paddingTop(display)) + "px";
       node.style.left = node.style.right = "";
       if (horiz == "right") {
         left = display.sizer.clientWidth - node.offsetWidth;
@@ -3123,6 +3044,9 @@ window.CodeMirror = (function() {
       this.refresh();
     },
 
+    on: function(type, f) {on(this, type, f);},
+    off: function(type, f) {off(this, type, f);},
+
     operation: function(f){return runInOp(this, f);},
 
     refresh: operation(null, function() {
@@ -3146,7 +3070,6 @@ window.CodeMirror = (function() {
     getScrollerElement: function(){return this.display.scroller;},
     getGutterElement: function(){return this.display.gutters;}
   };
-  eventMixin(CodeMirror);
 
   // OPTION DEFAULTS
 
@@ -3271,7 +3194,7 @@ window.CodeMirror = (function() {
   };
 
   CodeMirror.getMode = function(options, spec) {
-    var spec = CodeMirror.resolveMode(spec);
+    spec = CodeMirror.resolveMode(spec);
     var mfactory = modes[spec.name];
     if (!mfactory) return CodeMirror.getMode(options, "text/plain");
     var modeObj = mfactory(options, spec);
@@ -3284,7 +3207,6 @@ window.CodeMirror = (function() {
       }
     }
     modeObj.name = spec.name;
-
     return modeObj;
   };
 
@@ -3312,12 +3234,6 @@ window.CodeMirror = (function() {
   var initHooks = [];
   CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
 
-  var helpers = CodeMirror.helpers = {};
-  CodeMirror.registerHelper = function(type, name, value) {
-    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
-    helpers[type][name] = value;
-  };
-
   // MODE STATE HANDLING
 
   // Utility functions for working with state. Exported because modes
@@ -3363,10 +3279,6 @@ window.CodeMirror = (function() {
       var l = cm.getCursor().line;
       cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
     },
-    delLineLeft: function(cm) {
-      var cur = cm.getCursor();
-      cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
-    },
     undo: function(cm) {cm.undo();},
     redo: function(cm) {cm.redo();},
     goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
@@ -3462,7 +3374,7 @@ window.CodeMirror = (function() {
     "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
     "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
     "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
-    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
+    "Cmd-[": "indentLess", "Cmd-]": "indentMore",
     fallthrough: ["basic", "emacsy"]
   };
   keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -3501,7 +3413,7 @@ window.CodeMirror = (function() {
 
     for (var i = 0; i < maps.length; ++i) {
       var done = lookup(maps[i]);
-      if (done) return done != "stop";
+      if (done) return done;
     }
   }
   function isModifierKey(event) {
@@ -3655,7 +3567,6 @@ window.CodeMirror = (function() {
     this.doc = doc;
   }
   CodeMirror.TextMarker = TextMarker;
-  eventMixin(TextMarker);
 
   TextMarker.prototype.clear = function() {
     if (this.explicitlyCleared) return;
@@ -3684,7 +3595,7 @@ window.CodeMirror = (function() {
     if (min != null && cm) regChange(cm, min, max + 1);
     this.lines.length = 0;
     this.explicitlyCleared = true;
-    if (this.atomic && this.doc.cantEdit) {
+    if (this.collapsed && this.doc.cantEdit) {
       this.doc.cantEdit = false;
       if (cm) reCheckSelection(cm);
     }
@@ -3747,7 +3658,6 @@ window.CodeMirror = (function() {
     if (marker.replacedWith) {
       marker.collapsed = true;
       marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
-      if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
     }
     if (marker.collapsed) sawCollapsedSpans = true;
 
@@ -3790,7 +3700,7 @@ window.CodeMirror = (function() {
     }
     if (cm) {
       if (updateMaxLine) cm.curOp.updateMaxLine = true;
-      if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
+      if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
         regChange(cm, from.line, to.line + 1);
       if (marker.atomic) reCheckSelection(cm);
     }
@@ -3808,7 +3718,6 @@ window.CodeMirror = (function() {
     }
   }
   CodeMirror.SharedTextMarker = SharedTextMarker;
-  eventMixin(SharedTextMarker);
 
   SharedTextMarker.prototype.clear = function() {
     if (this.explicitlyCleared) return;
@@ -3922,13 +3831,6 @@ window.CodeMirror = (function() {
         }
       }
     }
-    if (sameLine && first) {
-      // Make sure we didn't create any zero-length spans
-      for (var i = 0; i < first.length; ++i)
-        if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
-          first.splice(i--, 1);
-      if (!first.length) first = null;
-    }
 
     var newMarkers = [first];
     if (!sameLine) {
@@ -4023,7 +3925,6 @@ window.CodeMirror = (function() {
       sp = sps[i];
       if (!sp.marker.collapsed) continue;
       if (sp.from == null) return true;
-      if (sp.marker.replacedWith) continue;
       if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
         return true;
     }
@@ -4037,7 +3938,7 @@ window.CodeMirror = (function() {
       return true;
     for (var sp, i = 0; i < line.markedSpans.length; ++i) {
       sp = line.markedSpans[i];
-      if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
+      if (sp.marker.collapsed && sp.from == span.to &&
           (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
           lineIsHiddenInner(doc, line, sp)) return true;
     }
@@ -4061,12 +3962,11 @@ window.CodeMirror = (function() {
   // LINE WIDGETS
 
   var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
-    if (options) for (var opt in options) if (options.hasOwnProperty(opt))
+    for (var opt in options) if (options.hasOwnProperty(opt))
       this[opt] = options[opt];
     this.cm = cm;
     this.node = node;
   };
-  eventMixin(LineWidget);
   function widgetOperation(f) {
     return function() {
       var withOp = !this.cm.curOp;
@@ -4081,9 +3981,7 @@ window.CodeMirror = (function() {
     if (no == null || !ws) return;
     for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
     if (!ws.length) this.line.widgets = null;
-    var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop;
     updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
-    if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
     regChange(this.cm, no, no + 1);
   });
   LineWidget.prototype.changed = widgetOperation(function() {
@@ -4107,12 +4005,10 @@ window.CodeMirror = (function() {
     var widget = new LineWidget(cm, node, options);
     if (widget.noHScroll) cm.display.alignWidgets = true;
     changeLine(cm, handle, function(line) {
-      var widgets = line.widgets || (line.widgets = []);
-      if (widget.insertAt == null) widgets.push(widget);
-      else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
+      (line.widgets || (line.widgets = [])).push(widget);
       widget.line = line;
       if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
-        var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop;
+        var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
         updateLineHeight(line, line.height + widgetHeight(widget));
         if (aboveVisible) addToScrollPos(cm, 0, widget.height);
       }
@@ -4125,12 +4021,12 @@ window.CodeMirror = (function() {
 
   // Line objects. These hold state related to a line, including
   // highlighting info (the styles array).
-  var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
-    this.text = text;
-    attachMarkedSpans(this, markedSpans);
-    this.height = estimateHeight ? estimateHeight(this) : 1;
-  };
-  eventMixin(Line);
+  function makeLine(text, markedSpans, estimateHeight) {
+    var line = {text: text};
+    attachMarkedSpans(line, markedSpans);
+    line.height = estimateHeight ? estimateHeight(line) : 1;
+    return line;
+  }
 
   function updateLine(line, text, markedSpans, estimateHeight) {
     line.text = text;
@@ -4154,7 +4050,7 @@ window.CodeMirror = (function() {
   function runMode(cm, text, mode, state, f) {
     var flattenSpans = mode.flattenSpans;
     if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
-    var curStart = 0, curStyle = null;
+    var curText = "", curStyle = null;
     var stream = new StringStream(text, cm.options.tabSize), style;
     if (text == "" && mode.blankLine) mode.blankLine(state);
     while (!stream.eol()) {
@@ -4166,13 +4062,14 @@ window.CodeMirror = (function() {
       } else {
         style = mode.token(stream, state);
       }
-      if (!flattenSpans || curStyle != style) {
-        if (curStart < stream.start) f(stream.start, curStyle);
-        curStart = stream.start; curStyle = style;
-      }
+      var substr = stream.current();
       stream.start = stream.pos;
+      if (!flattenSpans || curStyle != style) {
+        if (curText) f(curText, curStyle);
+        curText = substr; curStyle = style;
+      } else curText = curText + substr;
     }
-    if (curStart < stream.pos) f(stream.pos, curStyle);
+    if (curText) f(curText, curStyle);
   }
 
   function highlightLine(cm, line, state) {
@@ -4180,24 +4077,27 @@ window.CodeMirror = (function() {
     // mode/overlays that it is based on (for easy invalidation).
     var st = [cm.state.modeGen];
     // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
+    runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
 
     // Run overlays, adjust style array.
     for (var o = 0; o < cm.state.overlays.length; ++o) {
-      var overlay = cm.state.overlays[o], i = 1, at = 0;
-      runMode(cm, line.text, overlay.mode, true, function(end, style) {
-        var start = i;
+      var overlay = cm.state.overlays[o], i = 1;
+      runMode(cm, line.text, overlay.mode, true, function(txt, style) {
+        var start = i, len = txt.length;
         // Ensure there's a token end at the current position, and that i points at it
-        while (at < end) {
-          var i_end = st[i];
-          if (i_end > end)
-            st.splice(i, 1, end, st[i+1], i_end);
+        while (len) {
+          var cur = st[i], len_ = cur.length;
+          if (len_ <= len) {
+            len -= len_;
+          } else {
+            st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
+            len = 0;
+          }
           i += 2;
-          at = Math.min(end, i_end);
         }
         if (!style) return;
         if (overlay.opaque) {
-          st.splice(start, i - start, end, style);
+          st.splice(start, i - start, txt, style);
           i = start + 2;
         } else {
           for (; start < i; start += 2) {
@@ -4237,31 +4137,37 @@ window.CodeMirror = (function() {
   }
 
   function lineContent(cm, realLine, measure) {
-    var merged, line = realLine, empty = true;
-    while (merged = collapsedSpanAtStart(line))
+    var merged, line = realLine, lineBefore, sawBefore, simple = true;
+    while (merged = collapsedSpanAtStart(line)) {
+      simple = false;
       line = getLine(cm.doc, merged.find().from.line);
+      if (!lineBefore) lineBefore = line;
+    }
 
     var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
-                   measure: null, measuredSomething: false, cm: cm};
+                   measure: null, addedOne: false, cm: cm};
     if (line.textClass) builder.pre.className = line.textClass;
 
     do {
-      if (line.text) empty = false;
       builder.measure = line == realLine && measure;
       builder.pos = 0;
       builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
       if ((ie || webkit) && cm.getOption("lineWrapping"))
         builder.addToken = buildTokenSplitSpaces(builder.addToken);
-      var next = insertLineContent(line, builder, getLineStyles(cm, line));
-      if (measure && line == realLine && !builder.measuredSomething) {
+      if (measure && sawBefore && line != realLine && !builder.addedOne) {
         measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
-        builder.measuredSomething = true;
+        builder.addedOne = true;
+      }
+      var next = insertLineContent(line, builder, getLineStyles(cm, line));
+      sawBefore = line == lineBefore;
+      if (next) {
+        line = getLine(cm.doc, next.to.line);
+        simple = false;
       }
-      if (next) line = getLine(cm.doc, next.to.line);
     } while (next);
 
-    if (measure && !builder.measuredSomething && !measure[0])
-      measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
+    if (measure && !builder.addedOne)
+      measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
     if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
       builder.pre.appendChild(document.createTextNode("\u00a0"));
 
@@ -4285,7 +4191,7 @@ window.CodeMirror = (function() {
   }
 
   var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
-  function buildToken(builder, text, style, startStyle, endStyle, title) {
+  function buildToken(builder, text, style, startStyle, endStyle) {
     if (!text) return;
     if (!tokenSpecialChars.test(text)) {
       builder.col += text.length;
@@ -4318,9 +4224,7 @@ window.CodeMirror = (function() {
       var fullStyle = style || "";
       if (startStyle) fullStyle += startStyle;
       if (endStyle) fullStyle += endStyle;
-      var token = elt("span", [content], fullStyle);
-      if (title) token.title = title;
-      return builder.pre.appendChild(token);
+      return builder.pre.appendChild(elt("span", [content], fullStyle));
     }
     builder.pre.appendChild(content);
   }
@@ -4335,11 +4239,9 @@ window.CodeMirror = (function() {
       } else if (i && wrapping && spanAffectsWrapping(text, i)) {
         builder.pre.appendChild(elt("wbr"));
       }
-      var old = builder.measure[builder.pos];
       var span = builder.measure[builder.pos] =
         buildToken(builder, ch, style,
                    start && startStyle, i == text.length - 1 && endStyle);
-      if (old) span.leftSide = old.leftSide || old;
       // In IE single-space nodes wrap differently than spaces
       // embedded in larger text nodes, except when set to
       // white-space: normal (issue #1268).
@@ -4348,7 +4250,7 @@ window.CodeMirror = (function() {
         span.style.whiteSpace = "normal";
       builder.pos += ch.length;
     }
-    if (text.length) builder.measuredSomething = true;
+    if (text.length) builder.addedOne = true;
   }
 
   function buildTokenSplitSpaces(inner) {
@@ -4358,27 +4260,18 @@ window.CodeMirror = (function() {
       out += " ";
       return out;
     }
-    return function(builder, text, style, startStyle, endStyle, title) {
-      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
+    return function(builder, text, style, startStyle, endStyle) {
+      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
     };
   }
 
-  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
-    var widget = !ignoreWidget && marker.replacedWith;
+  function buildCollapsedSpan(builder, size, widget) {
     if (widget) {
       if (!builder.display) widget = widget.cloneNode(true);
       builder.pre.appendChild(widget);
-      if (builder.measure) {
-        if (size) {
-          builder.measure[builder.pos] = widget;
-        } else {
-          var elt = builder.measure[builder.pos] = zeroWidthElement(builder.cm.display.measure);
-          if (marker.type != "bookmark" || marker.insertLeft)
-            builder.pre.insertBefore(elt, widget);
-          else
-            builder.pre.appendChild(elt);
-        }
-        builder.measuredSomething = true;
+      if (builder.measure && size) {
+        builder.measure[builder.pos] = widget;
+        builder.addedOne = true;
       }
     }
     builder.pos += size;
@@ -4387,18 +4280,19 @@ window.CodeMirror = (function() {
   // Outputs a number of spans to make up a line, taking highlighting
   // and marked text into account.
   function insertLineContent(line, builder, styles) {
-    var spans = line.markedSpans, allText = line.text, at = 0;
+    var spans = line.markedSpans;
     if (!spans) {
       for (var i = 1; i < styles.length; i+=2)
-        builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
+        builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
       return;
     }
 
-    var len = allText.length, pos = 0, i = 1, text = "", style;
-    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
+    var allText = line.text, len = allText.length;
+    var pos = 0, i = 1, text = "", style;
+    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
     for (;;) {
       if (nextChange == pos) { // Update current marker set
-        spanStyle = spanEndStyle = spanStartStyle = title = "";
+        spanStyle = spanEndStyle = spanStartStyle = "";
         collapsed = null; nextChange = Infinity;
         var foundBookmark = null;
         for (var j = 0; j < spans.length; ++j) {
@@ -4408,17 +4302,17 @@ window.CodeMirror = (function() {
             if (m.className) spanStyle += " " + m.className;
             if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
             if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
-            if (m.title && !title) title = m.title;
-            if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
+            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;
+          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.marker, collapsed.from == null);
+                             collapsed.from != null && collapsed.marker.replacedWith);
           if (collapsed.to == null) return collapsed.marker.find();
         }
         if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
@@ -4432,14 +4326,13 @@ window.CodeMirror = (function() {
           if (!collapsed) {
             var tokenText = end > upto ? text.slice(0, upto - pos) : text;
             builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
-                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
+                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
           }
           if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
           pos = end;
           spanStartStyle = "";
         }
-        text = allText.slice(at, at = styles[i++]);
-        style = styleToClass(styles[i++]);
+        text = styles[i++]; style = styleToClass(styles[i++]);
       }
     }
   }
@@ -4462,7 +4355,7 @@ window.CodeMirror = (function() {
       // This is a whole-line replace. Treated specially to make
       // sure line objects move the way they are supposed to.
       for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
-        added.push(new Line(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(text[i], spansFor(i), estimateHeight));
       update(lastLine, lastLine.text, lastSpans);
       if (nlines) doc.remove(from.line, nlines);
       if (added.length) doc.insert(from.line, added);
@@ -4471,8 +4364,8 @@ window.CodeMirror = (function() {
         update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
       } else {
         for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
-          added.push(new Line(text[i], spansFor(i), estimateHeight));
-        added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
+          added.push(makeLine(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
         update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
         doc.insert(from.line + 1, added);
       }
@@ -4483,7 +4376,7 @@ window.CodeMirror = (function() {
       update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
       update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
       for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
-        added.push(new Line(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(text[i], spansFor(i), estimateHeight));
       if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
       doc.insert(from.line + 1, added);
     }
@@ -4626,12 +4519,11 @@ window.CodeMirror = (function() {
     if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
     if (firstLine == null) firstLine = 0;
 
-    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
+    BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
     this.first = firstLine;
     this.scrollTop = this.scrollLeft = 0;
     this.cantEdit = false;
     this.history = makeHistory();
-    this.cleanGeneration = 1;
     this.frontier = firstLine;
     var start = Pos(firstLine, 0);
     this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
@@ -4691,11 +4583,6 @@ window.CodeMirror = (function() {
     getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
     getLineNumber: function(line) {return lineNo(line);},
 
-    getLineHandleVisualStart: function(line) {
-      if (typeof line == "number") line = getLine(this, line);
-      return visualLine(this, line);
-    },
-
     lineCount: function() {return this.size;},
     firstLine: function() {return this.first;},
     lastLine: function() {return this.first + this.size - 1;},
@@ -4737,25 +4624,20 @@ window.CodeMirror = (function() {
       var hist = this.history;
       return {undo: hist.done.length, redo: hist.undone.length};
     },
-    clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
+    clearHistory: function() {this.history = makeHistory();},
 
     markClean: function() {
-      this.cleanGeneration = this.changeGeneration();
-    },
-    changeGeneration: function() {
+      this.history.dirtyCounter = 0;
       this.history.lastOp = this.history.lastOrigin = null;
-      return this.history.generation;
-    },
-    isClean: function (gen) {
-      return this.history.generation == (gen || this.cleanGeneration);
     },
+    isClean: function () {return this.history.dirtyCounter == 0;},
 
     getHistory: function() {
       return {done: copyHistoryArray(this.history.done),
               undone: copyHistoryArray(this.history.undone)};
     },
     setHistory: function(histData) {
-      var hist = this.history = makeHistory(this.history.maxGeneration);
+      var hist = this.history = makeHistory();
       hist.done = histData.done.slice(0);
       hist.undone = histData.undone.slice(0);
     },
@@ -4866,8 +4748,6 @@ window.CodeMirror = (function() {
       return function() {return method.apply(this.doc, arguments);};
     })(Doc.prototype[prop]);
 
-  eventMixin(Doc);
-
   function linkedDocs(doc, f, sharedHistOnly) {
     function propagate(doc, skip, sharedHist) {
       if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
@@ -4987,7 +4867,7 @@ window.CodeMirror = (function() {
 
   // HISTORY
 
-  function makeHistory(startGen) {
+  function makeHistory() {
     return {
       // Arrays of history events. Doing something adds an event to
       // done and clears undo. Undoing moves events from done to
@@ -4997,7 +4877,7 @@ window.CodeMirror = (function() {
       // event
       lastTime: 0, lastOp: null, lastOrigin: null,
       // Used by the isClean() method
-      generation: startGen || 1, maxGeneration: startGen || 1
+      dirtyCounter: 0
     };
   }
 
@@ -5011,8 +4891,7 @@ window.CodeMirror = (function() {
   }
 
   function historyChangeFromChange(doc, change) {
-    var from = { line: change.from.line, ch: change.from.ch };
-    var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+    var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
     attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
     linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
     return histChange;
@@ -5042,13 +4921,17 @@ window.CodeMirror = (function() {
     } else {
       // Can not be merged, start a new event.
       cur = {changes: [historyChangeFromChange(doc, change)],
-             generation: hist.generation,
              anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
              anchorAfter: selAfter.anchor, headAfter: selAfter.head};
       hist.done.push(cur);
-      hist.generation = ++hist.maxGeneration;
       while (hist.done.length > hist.undoDepth)
         hist.done.shift();
+      if (hist.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.
+        hist.dirtyCounter = NaN;
+      else
+        hist.dirtyCounter++;
     }
     hist.lastTime = time;
     hist.lastOp = opId;
@@ -5163,9 +5046,6 @@ window.CodeMirror = (function() {
     if (e.stopPropagation) e.stopPropagation();
     else e.cancelBubble = true;
   }
-  function e_defaultPrevented(e) {
-    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
-  }
   function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
   CodeMirror.e_stop = e_stop;
   CodeMirror.e_preventDefault = e_preventDefault;
@@ -5232,11 +5112,6 @@ window.CodeMirror = (function() {
       delayedCallbacks.push(bnd(arr[i]));
   }
 
-  function signalDOMEvent(cm, e) {
-    signal(cm, e.type, cm, e);
-    return e_defaultPrevented(e);
-  }
-
   function fireDelayed() {
     --delayedCallbackDepth;
     var delayed = delayedCallbacks;
@@ -5251,11 +5126,6 @@ window.CodeMirror = (function() {
 
   CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
 
-  function eventMixin(ctor) {
-    ctor.prototype.on = function(type, f) {on(this, type, f);};
-    ctor.prototype.off = function(type, f) {off(this, type, f);};
-  }
-
   // MISC UTILITIES
 
   // Number of pixels added to scroller and sizer to hide scrollbar
@@ -5296,11 +5166,7 @@ window.CodeMirror = (function() {
     if (ios) { // Mobile Safari apparently has a bug where select() is broken.
       node.selectionStart = 0;
       node.selectionEnd = node.value.length;
-    } else {
-      // Suppress mysterious IE10 errors
-      try { node.select(); }
-      catch(_e) {}
-    }
+    } else node.select();
   }
 
   function indexOf(collection, elt) {
@@ -5405,15 +5271,15 @@ window.CodeMirror = (function() {
     };
   else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
     spanAffectsWrapping = function(str, i) {
-      return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
+      var result = /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
+      return result;
     };
-  else if (webkit && !/Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
+  else if (webkit)
     spanAffectsWrapping = function(str, i) {
-      if (i > 1 && str.charCodeAt(i - 1) == 45) {
-        if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
-        if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
-      }
-      return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
+      if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
+        return true;
+      var result = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|\?[\w~`@#$%\^&*(_=+{[|><]/.test(str.slice(i - 1, i + 1));
+      return result;
     };
 
   var knownScrollbarWidth;
@@ -5499,15 +5365,11 @@ window.CodeMirror = (function() {
 
   function iterateBidiSections(order, from, to, f) {
     if (!order) return f(from, to, "ltr");
-    var found = false;
     for (var i = 0; i < order.length; ++i) {
       var part = order[i];
-      if (part.from < to && part.to > from || from == to && part.to == from) {
+      if (part.from < to && part.to > from || from == to && part.to == from)
         f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
-        found = true;
-      }
     }
-    if (!found) f(from, to, "ltr");
   }
 
   function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
@@ -5769,7 +5631,7 @@ window.CodeMirror = (function() {
 
   // THE END
 
-  CodeMirror.version = "3.14.1";
+  CodeMirror.version = "3.13";
 
   return CodeMirror;
 })();
index 4f54b0c..509d920 100644 (file)
@@ -339,8 +339,7 @@ CodeMirror.defineMode('coffeescript', function(conf) {
             return state.scopes[0].offset;
         },
 
-        lineComment: "#",
-        fold: "indent"
+        lineComment: "#"
     };
     return external;
 });
index cd2123e..4f590f2 100644 (file)
@@ -17,7 +17,7 @@
 
   CodeMirror.defineExtension("lineComment", function(from, to, options) {
     if (!options) options = noOptions;
-    var self = this, mode = self.getModeAt(from);
+    var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode;
     var commentString = options.lineComment || mode.lineComment;
     if (!commentString) {
       if (options.blockCommentStart || mode.blockCommentStart) {
@@ -30,7 +30,7 @@
     if (firstLine == null) return;
     var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
     var pad = options.padding == null ? " " : options.padding;
-    var blankLines = options.commentBlankLines || from.line == to.line;
+    var blankLines = options.commentBlankLines;
 
     self.operation(function() {
       if (options.indent) {
@@ -52,7 +52,7 @@
 
   CodeMirror.defineExtension("blockComment", function(from, to, options) {
     if (!options) options = noOptions;
-    var self = this, mode = self.getModeAt(from);
+    var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode;
     var startString = options.blockCommentStart || mode.blockCommentStart;
     var endString = options.blockCommentEnd || mode.blockCommentEnd;
     if (!startString || !endString) {
 
   CodeMirror.defineExtension("uncomment", function(from, to, options) {
     if (!options) options = noOptions;
-    var self = this, mode = self.getModeAt(from);
+    var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode;
     var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end);
 
     // Try finding line comments
     var lineString = options.lineComment || mode.lineComment, lines = [];
-    var pad = options.padding == null ? " " : options.padding, didSomething;
-    lineComment: {
-      if (!lineString) break lineComment;
+    var pad = options.padding == null ? " " : options.padding;
+    lineComment: for(;;) {
+      if (!lineString) break;
       for (var i = start; i <= end; ++i) {
         var line = self.getLine(i);
         var found = line.indexOf(lineString);
         if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment;
-        if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
+        if (i != start && nonWS.test(line.slice(0, found))) break lineComment;
         lines.push(line);
       }
       self.operation(function() {
           var pos = line.indexOf(lineString), endPos = pos + lineString.length;
           if (pos < 0) continue;
           if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
-          didSomething = true;
           self.replaceRange("", Pos(i, pos), Pos(i, endPos));
         }
       });
-      if (didSomething) return true;
+      return true;
     }
 
     // Try block comments
index b52e787..27c97f3 100644 (file)
@@ -103,8 +103,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
     startState: function(base) {
       return {tokenize: tokenBase,
               baseIndent: base || 0,
-              stack: [],
-              lastToken: null};
+              stack: []};
     },
 
     token: function(stream, state) {
@@ -164,7 +163,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
       var context = state.stack[state.stack.length-1];
       if (style == "variable") {
         if (type == "variable-definition") state.stack.push("propertyValue");
-        return state.lastToken = "variable-2";
+        return "variable-2";
       } else if (style == "property") {
         var word = stream.current().toLowerCase();
         if (context == "propertyValue") {
@@ -274,10 +273,10 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
       else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
       else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
       else if (context == "@mediaType(" && type == ")") state.stack.pop();
-      else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
+      else if ((context == "rule" || context == "block") && type == ":") state.stack.push("propertyValue");
       else if (context == "propertyValue" && type == ";") state.stack.pop();
       else if (context == "@import" && type == ";") state.stack.pop();
-      return state.lastToken = style;
+      return style;
     },
 
     indent: function(state, textAfter) {
@@ -289,8 +288,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
 
     electricChars: "}",
     blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    fold: "brace"
+    blockCommentEnd: "*/"
   };
 });
 
index 3d04603..fabe1c4 100644 (file)
@@ -2,7 +2,6 @@
 
 CodeMirror.defineMode("javascript", function(config, parserConfig) {
   var indentUnit = config.indentUnit;
-  var statementIndent = parserConfig.statementIndent;
   var jsonMode = parserConfig.json;
   var isTS = parserConfig.typescript;
 
@@ -227,9 +226,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   }
   function pushlex(type, info) {
     var result = function() {
-      var state = cx.state, indent = state.indented;
-      if (state.lexical.type == "stat") indent = state.lexical.indented;
-      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
+      var state = cx.state;
+      state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
     };
     result.lex = true;
     return result;
@@ -258,32 +256,31 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
     if (type == "{") return cont(pushlex("}"), block, poplex);
     if (type == ";") return cont();
-    if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+    if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse(cx.state.indented));
     if (type == "function") return cont(functiondef);
     if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
-                                   poplex, statement, poplex);
+                                      poplex, statement, poplex);
     if (type == "variable") return cont(pushlex("stat"), maybelabel);
     if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
-                                      block, poplex, poplex);
+                                         block, poplex, poplex);
     if (type == "case") return cont(expression, expect(":"));
     if (type == "default") return cont(expect(":"));
     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
-                                     statement, poplex, popcontext);
+                                        statement, poplex, popcontext);
     return pass(pushlex("stat"), expression, expect(";"), poplex);
   }
   function expression(type) {
-    return expressionInner(type, false);
+    return expressionInner(type, maybeoperatorComma);
   }
   function expressionNoComma(type) {
-    return expressionInner(type, true);
+    return expressionInner(type, maybeoperatorNoComma);
   }
-  function expressionInner(type, noComma) {
-    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+  function expressionInner(type, maybeop) {
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (type == "function") return cont(functiondef);
-    if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+    if (type == "keyword c") return cont(maybeexpression);
     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
-    if (type == "operator") return cont(noComma ? expressionNoComma : expression);
+    if (type == "operator") return cont(expression);
     if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
     if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
     return cont();
@@ -292,13 +289,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type.match(/[;\}\)\],]/)) return pass();
     return pass(expression);
   }
-  function maybeexpressionNoComma(type) {
-    if (type.match(/[;\}\)\],]/)) return pass();
-    return pass(expressionNoComma);
-  }
 
   function maybeoperatorComma(type, value) {
-    if (type == ",") return cont(expression);
+    if (type == ",") return pass();
     return maybeoperatorNoComma(type, value, maybeoperatorComma);
   }
   function maybeoperatorNoComma(type, value, me) {
@@ -311,7 +304,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == ";") return;
     if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
     if (type == ".") return cont(property, me);
-    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+    if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, me);
   }
   function maybelabel(type) {
     if (type == ":") return cont(poplex, statement);
@@ -373,8 +366,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (value == "=") return cont(expressionNoComma, vardef2);
     if (type == ",") return cont(vardef1);
   }
-  function maybeelse(type, value) {
-    if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
+  function maybeelse(indent) {
+    return function(type, value) {
+      if (type == "keyword b" && value == "else") {
+        cx.state.lexical = new JSLexical(indent, 0, "form", null, cx.state.lexical);
+        return cont(statement, poplex);
+      }
+      return pass();
+    };
   }
   function forspec1(type) {
     if (type == "var") return cont(vardef1, expect(";"), forspec2);
@@ -435,23 +434,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
       if (state.tokenize != jsTokenBase) return 0;
       var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
-      // Kludge to prevent 'maybelse' from blocking lexical scope pops
-      for (var i = state.cc.length - 1; i >= 0; --i) {
-        var c = state.cc[i];
-        if (c == poplex) lexical = lexical.prev;
-        else if (c != maybeelse || /^else\b/.test(textAfter)) break;
-      }
       if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
-      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
-        lexical = lexical.prev;
       var type = lexical.type, closing = firstChar == type;
+      if (parserConfig.statementIndent != null) {
+        if (type == ")" && lexical.prev && lexical.prev.type == "stat") lexical = lexical.prev;
+        if (lexical.type == "stat") return lexical.indented + parserConfig.statementIndent;
+      }
 
       if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "stat")
-        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
-      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
+        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
+      else if (lexical.info == "switch" && !closing)
         return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
       else if (lexical.align) return lexical.column + (closing ? 0 : 1);
       else return lexical.indented + (closing ? 0 : indentUnit);
@@ -461,9 +456,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     blockCommentStart: jsonMode ? null : "/*",
     blockCommentEnd: jsonMode ? null : "*/",
     lineComment: jsonMode ? null : "//",
-    fold: "brace",
 
-    helperType: jsonMode ? "json" : "javascript",
     jsonMode: jsonMode
   };
 });
index 131fe83..e4ff914 100644 (file)
@@ -5,26 +5,25 @@
   var Pos = CodeMirror.Pos;
 
   var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
-  function findMatchingBracket(cm, where, strict) {
-    var state = cm.state.matchBrackets;
-    var maxScanLen = (state && state.maxScanLineLength) || 10000;
+  function findMatchingBracket(cm) {
+    var maxScanLen = cm.state._matchBrackets.maxScanLineLength || 10000;
 
-    var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+    var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
     var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
     if (!match) return null;
     var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
-    if (strict && forward != (pos == cur.ch)) return null;
-    var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1));
+    var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
 
     var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
     function scan(line, lineNo, start) {
       if (!line.text) return;
       var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
       if (line.text.length > maxScanLen) return null;
+      var checkTokenStyles = line.text.length < 1000;
       if (start != null) pos = start + d;
       for (; pos != end; pos += d) {
         var ch = line.text.charAt(pos);
-        if (re.test(ch) && cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) {
+        if (re.test(ch) && (!checkTokenStyles || cm.getTokenAt(Pos(lineNo, pos + 1)).type == style)) {
           var match = matching[ch];
           if (match.charAt(1) == ">" == forward) stack.push(ch);
           else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
       else found = scan(cm.getLineHandle(i), i);
       if (found) break;
     }
-    return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos),
-            match: found && found.match, forward: forward};
+    return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
   }
 
   function matchBrackets(cm, autoclear) {
     // Disable brace matching in long lines, since it'll cause hugely slow updates
-    var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
+    var maxHighlightLen = cm.state._matchBrackets.maxHighlightLineLength || 1000;
     var found = findMatchingBracket(cm);
     if (!found || cm.getLine(found.from.line).length > maxHighlightLen ||
        found.to && cm.getLine(found.to.line).length > maxHighlightLen)
     if (old && old != CodeMirror.Init)
       cm.off("cursorActivity", doMatchBrackets);
     if (val) {
-      cm.state.matchBrackets = typeof val == "object" ? val : {};
+      cm.state._matchBrackets = typeof val == "object" ? val : {};
       cm.on("cursorActivity", doMatchBrackets);
     }
   });
 
   CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
-  CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){
-    return findMatchingBracket(this, pos, strict);
-  });
+  CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
 })();
index 9016cc7..066db97 100644 (file)
@@ -19,64 +19,43 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
       if (result !== false) return result;
     }
 
-    if (support.hexNumber == true &&
-      ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
-      || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
+    if ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
+      || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/)) {
       // hex
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
       return "number";
-    } else if (support.binaryNumber == true &&
-      (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
-      || (ch == "0" && stream.match(/^b[01]+/)))) {
+    } else if (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
+      || (ch == "0" && stream.match(/^b[01]+/))) {
       // bitstring
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
       return "number";
     } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
       // numbers
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
-          stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
-      support.decimallessFloat == true && stream.eat('.');
+      stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
       return "number";
     } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
       // placeholders
       return "variable-3";
-    } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
+    } else if (ch == '"' || ch == "'") {
       // strings
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
       state.tokenize = tokenLiteral(ch);
       return state.tokenize(stream, state);
-    } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
-        || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
-        && (stream.peek() == "'" || stream.peek() == '"'))) {
-      // charset casting: _utf8'str', N'str', n'str'
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
-      return "keyword";
     } else if (/^[\(\),\;\[\]]/.test(ch)) {
       // no highlightning
       return null;
-    } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
-      // 1-line comment
-      stream.skipToEnd();
-      return "comment";
-    } else if ((support.commentHash && ch == "#")
-        || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
+    } else if (ch == "#" || (ch == "-" && stream.eat("-") && stream.eat(" "))) {
       // 1-line comments
-      // ref: https://kb.askmonty.org/en/comment-syntax/
       stream.skipToEnd();
       return "comment";
     } else if (ch == "/" && stream.eat("*")) {
       // multi-line comments
-      // ref: https://kb.askmonty.org/en/comment-syntax/
       state.tokenize = tokenComment;
       return state.tokenize(stream, state);
     } else if (ch == ".") {
       // .1 for 0.1
-      if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
+      if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e\d*)?|\d*e\d+)/i)) {
         return "number";
       }
       // .table_name (ODBC)
-      // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
-      if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
+      if (stream.match(/^[a-zA-Z_]+/) && support.ODBCdotTable == true) {
         return "variable-2";
       }
     } else if (operatorChars.test(ch)) {
@@ -86,13 +65,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     } else if (ch == '{' &&
         (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
       // dates (weird ODBC syntax)
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
       return "number";
     } else {
       stream.eatWhile(/^[_\w\d]/);
       var word = stream.current().toLowerCase();
       // dates (standard SQL syntax)
-      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
       if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
         return "number";
       if (atoms.hasOwnProperty(word)) return "atom";
@@ -189,8 +166,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
 
   // `identifier`
   function hookIdentifier(stream) {
-    // MySQL/MariaDB identifiers
-    // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
     var ch;
     while ((ch = stream.next()) != null) {
       if (ch == "`" && !stream.eat("`")) return "variable-2";
@@ -201,9 +176,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
   // variable token
   function hookVar(stream) {
     // variables
-    // @@prefix.varName @varName
-    // varName can be quoted with ` or ' or "
-    // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
+    // @@ and prefix
     if (stream.eat("@")) {
       stream.match(/^session\./);
       stream.match(/^local\./);
@@ -227,27 +200,18 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
 
   // short client keyword token
   function hookClient(stream) {
-    // \N means NULL
-    // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
-    if (stream.eat("N")) {
-        return "atom";
-    }
     // \g, etc
-    // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
-    return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
+    return stream.match(/^[a-zA-Z]\b/) ? "variable-2" : null;
   }
 
-  // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
   var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from having in insert into is join like not on or order select set table union update values where ";
 
-  // turn a space-separated list into an array
   function set(str) {
     var obj = {}, words = str.split(" ");
     for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
     return obj;
   }
 
-  // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
   CodeMirror.defineMIME("text/x-sql", {
     name: "sql",
     keywords: set(sqlKeywords + "begin"),
@@ -255,7 +219,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     atoms: set("false true null unknown"),
     operatorChars: /^[*+\-%<>!=]/,
     dateSQL: set("date time timestamp"),
-    support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
+    support: set("ODBCdotTable")
   });
 
   CodeMirror.defineMIME("text/x-mysql", {
@@ -266,7 +230,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     atoms: set("false true null unknown"),
     operatorChars: /^[*+\-%<>!=&|^]/,
     dateSQL: set("date time timestamp"),
-    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+    support: set("ODBCdotTable zerolessFloat"),
     hooks: {
       "@":   hookVar,
       "`":   hookIdentifier,
@@ -282,7 +246,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     atoms: set("false true null unknown"),
     operatorChars: /^[*+\-%<>!=&|^]/,
     dateSQL: set("date time timestamp"),
-    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+    support: set("ODBCdotTable zerolessFloat"),
     hooks: {
       "@":   hookVar,
       "`":   hookIdentifier,
@@ -290,20 +254,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     }
   });
 
-  // the query language used by Apache Cassandra is called CQL, but this mime type
-  // is called Cassandra to avoid confusion with Contextual Query Language
-  CodeMirror.defineMIME("text/x-cassandra", {
-    name: "sql",
-    client: { },
-    keywords: set("use select from using consistency where limit first reversed first and in insert into values using consistency ttl update set delete truncate begin batch apply create keyspace with columnfamily primary key index on drop alter type add any one quorum all local_quorum each_quorum"),
-    builtin: set("ascii bigint blob boolean counter decimal double float int text timestamp uuid varchar varint"),
-    atoms: set("false true"),
-    operatorChars: /^[<>=]/,
-    dateSQL: { },
-    support: set("commentSlashSlash decimallessFloat"),
-    hooks: { }
-  });
-
   // this is based on Peter Raganitsch's 'plsql' mode
   CodeMirror.defineMIME("text/x-plsql", {
     name:       "sql",
@@ -312,38 +262,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     functions:  set("abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"),
     builtin:    set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2"),
     operatorChars: /^[*+\-%<>!=~]/,
-    dateSQL:    set("date time timestamp"),
-    support:    set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
+    dateSQL:    set("date time timestamp")
   });
 }());
-
-/*
-  How Properties of Mime Types are used by SQL Mode
-  =================================================
-
-  keywords:
-    A list of keywords you want to be highlighted.
-  functions:
-    A list of function names you want to be highlighted.
-  builtin:
-    A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
-  operatorChars:
-    All characters that must be handled as operators.
-  client:
-    Commands parsed and executed by the client (not the server).
-  support:
-    A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
-    * ODBCdotTable: .tableName
-    * zerolessFloat: .1
-    * doubleQuote
-    * nCharCast: N'string'
-    * charsetCast: _utf8'string'
-    * commentHash: use # char for comments
-    * commentSlashSlash: use // for comments
-    * commentSpaceRequired: require a space after -- for comments
-  atoms:
-    Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
-    UNKNOWN, INFINITY, UNDERFLOW, NaN...
-  dateSQL:
-    Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
-*/
index 07db299..b04248c 100644 (file)
@@ -58,19 +58,20 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
         if (stream.eat("[")) {
           if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
           else return null;
-        } else if (stream.match("--")) {
-          return chain(inBlock("comment", "-->"));
-        else if (stream.match("DOCTYPE", true, true)) {
+        }
+        else if (stream.match("--")) return chain(inBlock("comment", "-->"));
+        else if (stream.match("DOCTYPE", true, true)) {
           stream.eatWhile(/[\w\._\-]/);
           return chain(doctype(1));
-        } else {
-          return null;
         }
-      } else if (stream.eat("?")) {
+        else return null;
+      }
+      else if (stream.eat("?")) {
         stream.eatWhile(/[\w\._\-]/);
         state.tokenize = inBlock("meta", "?>");
         return "meta";
-      } else {
+      }
+      else {
         var isClose = stream.eat("/");
         tagName = "";
         var c;
@@ -80,7 +81,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
         state.tokenize = inTag;
         return "tag";
       }
-    } else if (ch == "&") {
+    }
+    else if (ch == "&") {
       var ok;
       if (stream.eat("#")) {
         if (stream.eat("x")) {
@@ -92,7 +94,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
         ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
       }
       return ok ? "atom" : "error";
-    } else {
+    }
+    else {
       stream.eatWhile(/[^&<]/);
       return null;
     }
@@ -104,15 +107,16 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
       state.tokenize = inText;
       type = ch == ">" ? "endTag" : "selfcloseTag";
       return "tag";
-    } else if (ch == "=") {
+    }
+    else if (ch == "=") {
       type = "equals";
       return null;
-    } else if (ch == "<") {
-      return "error";
-    } else if (/[\'\"]/.test(ch)) {
+    }
+    else if (/[\'\"]/.test(ch)) {
       state.tokenize = inAttribute(ch);
       return state.tokenize(stream, state);
-    } else {
+    }
+    else {
       stream.eatWhile(/[^\s\u00a0=<>\"\']/);
       return "word";
     }
@@ -316,8 +320,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
     blockCommentStart: "<!--",
     blockCommentEnd: "-->",
 
-    configuration: parserConfig.htmlMode ? "html" : "xml",
-    helperType: parserConfig.htmlMode ? "html" : "xml"
+    configuration: parserConfig.htmlMode ? "html" : "xml"
   };
 });