codemirror-rails 3.13 → 3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +328 -250
  4. data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +7 -6
  5. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +33 -7
  6. data/vendor/assets/javascripts/codemirror/addons/edit/matchbrackets.js +14 -10
  7. data/vendor/assets/javascripts/codemirror/addons/edit/trailingspace.js +15 -0
  8. data/vendor/assets/javascripts/codemirror/addons/fold/brace-fold.js +70 -17
  9. data/vendor/assets/javascripts/codemirror/addons/fold/foldcode.js +56 -20
  10. data/vendor/assets/javascripts/codemirror/addons/fold/xml-fold.js +135 -39
  11. data/vendor/assets/javascripts/codemirror/addons/hint/html-hint.js +324 -571
  12. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +199 -109
  13. data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +60 -113
  14. data/vendor/assets/javascripts/codemirror/addons/lint/coffeescript-lint.js +24 -0
  15. data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +431 -0
  16. data/vendor/assets/javascripts/codemirror/addons/mode/multiplex.js +7 -1
  17. data/vendor/assets/javascripts/codemirror/addons/search/match-highlighter.js +46 -20
  18. data/vendor/assets/javascripts/codemirror/addons/search/search.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +370 -13
  20. data/vendor/assets/javascripts/codemirror/keymaps/extra.js +43 -0
  21. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +535 -214
  22. data/vendor/assets/javascripts/codemirror/modes/clike.js +56 -0
  23. data/vendor/assets/javascripts/codemirror/modes/javascript.js +19 -14
  24. data/vendor/assets/javascripts/codemirror/modes/markdown.js +2 -2
  25. data/vendor/assets/javascripts/codemirror/modes/ruby.js +67 -16
  26. data/vendor/assets/javascripts/codemirror/modes/smarty.js +167 -110
  27. data/vendor/assets/javascripts/codemirror/modes/sql.js +97 -15
  28. data/vendor/assets/javascripts/codemirror/modes/xml.js +14 -18
  29. data/vendor/assets/stylesheets/codemirror.css +0 -1
  30. data/vendor/assets/stylesheets/codemirror/addons/merge/merge.css +82 -0
  31. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f5ff9291537f44a79316e34c24d2d354e2f29c6
4
- data.tar.gz: dc676429b0b8522bb08b194d5b65715b0f58af6c
3
+ metadata.gz: e8db830325f2938ade6c18b47a5812733158244e
4
+ data.tar.gz: 018e82750853a395756b30bb752221b248239a7e
5
5
  SHA512:
6
- metadata.gz: ca0624b4dc29f738fe94f05c8e3b9f01d2f4aeee92432a5080d64d999700b82b98b05473e4e5cb450fb1631fb733eb5bfd609e796474638acc8639764b216611
7
- data.tar.gz: 7d91163dcafa7486d50bb46ab2c499129fc9ed74f6c969a460f99ebb47e469c513eda6c3316aff62f1552a834b93d6be530616ee525c59071876689f627ffe79
6
+ metadata.gz: 7897de28740b56bdecf9c8ba8571ac11eb74ff6224690d9aa689bd9488f2dbb8a750e0d31548df25b045ad212b977e880c5b440db371566cdfa76c6b535d4575
7
+ data.tar.gz: d9c73bfaf3cdca3cd320997bcff2759e3216afe96f2c2b979c7c5838c8ecb00a7c29f0ef094587a58d7874c990eea73540e7666f01127bacc653764de778c69c
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '3.13'
4
- CODEMIRROR_VERSION = '3.13'
3
+ VERSION = '3.14'
4
+ CODEMIRROR_VERSION = '3.14'
5
5
  end
6
6
  end
@@ -1,4 +1,4 @@
1
- // CodeMirror version 3.13
1
+ // CodeMirror version 3.14
2
2
  //
3
3
  // CodeMirror is the only global var we claim
4
4
  window.CodeMirror = (function() {
@@ -238,9 +238,10 @@ window.CodeMirror = (function() {
238
238
  }
239
239
 
240
240
  function keyMapChanged(cm) {
241
- var style = keyMap[cm.options.keyMap].style;
241
+ var map = keyMap[cm.options.keyMap], style = map.style;
242
242
  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
243
243
  (style ? " cm-keymap-" + style : "");
244
+ cm.state.disableInput = map.disableInput;
244
245
  }
245
246
 
246
247
  function themeChanged(cm) {
@@ -325,8 +326,8 @@ window.CodeMirror = (function() {
325
326
  d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
326
327
  d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
327
328
  var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
328
- var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
329
- var needsV = scrollHeight > d.scroller.clientHeight;
329
+ var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
330
+ var needsV = scrollHeight > (d.scroller.clientHeight + 1);
330
331
  if (needsV) {
331
332
  d.scrollbarV.style.display = "block";
332
333
  d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
@@ -513,9 +514,11 @@ window.CodeMirror = (function() {
513
514
  display.lastSizeC != display.wrapper.clientHeight;
514
515
  // This is just a bogus formula that detects when the editor is
515
516
  // resized or the font size changes.
516
- if (different) display.lastSizeC = display.wrapper.clientHeight;
517
+ if (different) {
518
+ display.lastSizeC = display.wrapper.clientHeight;
519
+ startWorker(cm, 400);
520
+ }
517
521
  display.showingFrom = from; display.showingTo = to;
518
- startWorker(cm, 100);
519
522
 
520
523
  var prevBottom = display.lineDiv.offsetTop;
521
524
  for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
@@ -604,8 +607,9 @@ window.CodeMirror = (function() {
604
607
  if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
605
608
  if (lineIsHidden(cm.doc, line)) {
606
609
  if (line.height != 0) updateLineHeight(line, 0);
607
- if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
608
- if (line.widgets[i].showIfHidden) {
610
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
611
+ var w = line.widgets[i];
612
+ if (w.showIfHidden) {
609
613
  var prev = cur.previousSibling;
610
614
  if (/pre/i.test(prev.nodeName)) {
611
615
  var wrap = elt("div", null, null, "position: relative");
@@ -613,9 +617,11 @@ window.CodeMirror = (function() {
613
617
  wrap.appendChild(prev);
614
618
  prev = wrap;
615
619
  }
616
- var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
617
- positionLineWidget(line.widgets[i], wnode, prev, dims);
620
+ var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
621
+ if (!w.handleMouseEvents) wnode.ignoreEvents = true;
622
+ positionLineWidget(w, wnode, prev, dims);
618
623
  }
624
+ }
619
625
  } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
620
626
  // This line is intact. Skip to the actual node. Update its
621
627
  // line number if needed.
@@ -658,25 +664,25 @@ window.CodeMirror = (function() {
658
664
 
659
665
  if (reuse) {
660
666
  reuse.alignable = null;
661
- var isOk = true, widgetsSeen = 0;
667
+ var isOk = true, widgetsSeen = 0, insertBefore = null;
662
668
  for (var n = reuse.firstChild, next; n; n = next) {
663
669
  next = n.nextSibling;
664
670
  if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
665
671
  reuse.removeChild(n);
666
672
  } else {
667
673
  for (var i = 0, first = true; i < line.widgets.length; ++i) {
668
- var widget = line.widgets[i], isFirst = false;
669
- if (!widget.above) { isFirst = first; first = false; }
674
+ var widget = line.widgets[i];
675
+ if (!widget.above) { insertBefore = n; first = false; }
670
676
  if (widget.node == n.firstChild) {
671
677
  positionLineWidget(widget, n, reuse, dims);
672
678
  ++widgetsSeen;
673
- if (isFirst) reuse.insertBefore(lineElement, n);
674
679
  break;
675
680
  }
676
681
  }
677
682
  if (i == line.widgets.length) { isOk = false; break; }
678
683
  }
679
684
  }
685
+ reuse.insertBefore(lineElement, insertBefore);
680
686
  if (isOk && widgetsSeen == line.widgets.length) {
681
687
  wrap = reuse;
682
688
  reuse.className = line.wrapClass || "";
@@ -711,6 +717,7 @@ window.CodeMirror = (function() {
711
717
  if (ie_lt8) wrap.style.zIndex = 2;
712
718
  if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
713
719
  var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
720
+ if (!widget.handleMouseEvents) node.ignoreEvents = true;
714
721
  positionLineWidget(widget, node, wrap, dims);
715
722
  if (widget.above)
716
723
  wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
@@ -793,74 +800,59 @@ window.CodeMirror = (function() {
793
800
  "px; height: " + (bottom - top) + "px"));
794
801
  }
795
802
 
796
- function drawForLine(line, fromArg, toArg, retTop) {
803
+ function drawForLine(line, fromArg, toArg) {
797
804
  var lineObj = getLine(doc, line);
798
- var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
799
- function coords(ch) {
800
- return charCoords(cm, Pos(line, ch), "div", lineObj);
805
+ var lineLen = lineObj.text.length;
806
+ var start, end;
807
+ function coords(ch, bias) {
808
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
801
809
  }
802
810
 
803
811
  iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
804
- var leftPos = coords(from), rightPos, left, right;
812
+ var leftPos = coords(from, "left"), rightPos, left, right;
805
813
  if (from == to) {
806
814
  rightPos = leftPos;
807
815
  left = right = leftPos.left;
808
816
  } else {
809
- rightPos = coords(to - 1);
817
+ rightPos = coords(to - 1, "right");
810
818
  if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
811
819
  left = leftPos.left;
812
820
  right = rightPos.right;
813
821
  }
822
+ if (fromArg == null && from == 0) left = pl;
814
823
  if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
815
824
  add(left, leftPos.top, null, leftPos.bottom);
816
825
  left = pl;
817
826
  if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
818
827
  }
819
828
  if (toArg == null && to == lineLen) right = clientWidth;
820
- if (fromArg == null && from == 0) left = pl;
821
- rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
829
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
830
+ start = leftPos;
831
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
832
+ end = rightPos;
822
833
  if (left < pl + 1) left = pl;
823
834
  add(left, rightPos.top, right - left, rightPos.bottom);
824
835
  });
825
- return rVal;
836
+ return {start: start, end: end};
826
837
  }
827
838
 
828
839
  if (sel.from.line == sel.to.line) {
829
840
  drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
830
841
  } else {
831
- var fromObj = getLine(doc, sel.from.line);
832
- var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
833
- while (merged = collapsedSpanAtEnd(cur)) {
834
- var found = merged.find();
835
- path.push(found.from.ch, found.to.line, found.to.ch);
836
- if (found.to.line == sel.to.line) {
837
- path.push(sel.to.ch);
838
- singleLine = true;
839
- break;
842
+ var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
843
+ var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
844
+ var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
845
+ var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
846
+ if (singleVLine) {
847
+ if (leftEnd.top < rightStart.top - 2) {
848
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
849
+ add(pl, rightStart.top, rightStart.left, rightStart.bottom);
850
+ } else {
851
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
840
852
  }
841
- cur = getLine(doc, found.to.line);
842
- }
843
-
844
- // This is a single, merged line
845
- if (singleLine) {
846
- for (var i = 0; i < path.length; i += 3)
847
- drawForLine(path[i], path[i+1], path[i+2]);
848
- } else {
849
- var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
850
- if (sel.from.ch)
851
- // Draw the first line of selection.
852
- middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
853
- else
854
- // Simply include it in the middle block.
855
- middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
856
-
857
- if (!sel.to.ch)
858
- middleBot = heightAtLine(cm, toObj) - display.viewOffset;
859
- else
860
- middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
861
-
862
- if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
863
853
  }
854
+ if (leftEnd.bottom < rightStart.top)
855
+ add(pl, leftEnd.bottom, null, rightStart.top);
864
856
  }
865
857
 
866
858
  removeChildrenAndAdd(display.selectionDiv, fragment);
@@ -926,12 +918,12 @@ window.CodeMirror = (function() {
926
918
  // valid state. If that fails, it returns the line with the
927
919
  // smallest indentation, which tends to need the least context to
928
920
  // parse correctly.
929
- function findStartLine(cm, n) {
921
+ function findStartLine(cm, n, precise) {
930
922
  var minindent, minline, doc = cm.doc;
931
923
  for (var search = n, lim = n - 100; search > lim; --search) {
932
924
  if (search <= doc.first) return doc.first;
933
925
  var line = getLine(doc, search - 1);
934
- if (line.stateAfter) return search;
926
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
935
927
  var indented = countColumn(line.text, null, cm.options.tabSize);
936
928
  if (minline == null || minindent > indented) {
937
929
  minline = search - 1;
@@ -941,10 +933,10 @@ window.CodeMirror = (function() {
941
933
  return minline;
942
934
  }
943
935
 
944
- function getStateBefore(cm, n) {
936
+ function getStateBefore(cm, n, precise) {
945
937
  var doc = cm.doc, display = cm.display;
946
938
  if (!doc.mode.startState) return true;
947
- var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
939
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
948
940
  if (!state) state = startState(doc.mode);
949
941
  else state = copyState(doc.mode, state);
950
942
  doc.iter(pos, n, function(line) {
@@ -965,7 +957,7 @@ window.CodeMirror = (function() {
965
957
  return e.offsetLeft;
966
958
  }
967
959
 
968
- function measureChar(cm, line, ch, data) {
960
+ function measureChar(cm, line, ch, data, bias) {
969
961
  var dir = -1;
970
962
  data = data || measureLine(cm, line);
971
963
 
@@ -974,9 +966,11 @@ window.CodeMirror = (function() {
974
966
  if (r) break;
975
967
  if (dir < 0 && pos == 0) dir = 1;
976
968
  }
969
+ var rightV = (pos < ch || bias == "right") && r.topRight != null;
977
970
  return {left: pos < ch ? r.right : r.left,
978
971
  right: pos > ch ? r.left : r.right,
979
- top: r.top, bottom: r.bottom};
972
+ top: rightV ? r.topRight : r.top,
973
+ bottom: rightV ? r.bottomRight : r.bottom};
980
974
  }
981
975
 
982
976
  function findCachedMeasurement(cm, line) {
@@ -1049,9 +1043,9 @@ window.CodeMirror = (function() {
1049
1043
  if (ie_lt9 && display.measure.first != pre)
1050
1044
  removeChildrenAndAdd(display.measure, pre);
1051
1045
 
1052
- for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1053
- var size = getRect(cur);
1054
- var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1046
+ function categorizeVSpan(top, bot) {
1047
+ if (bot > maxBot) bot = maxBot;
1048
+ if (top < 0) top = 0;
1055
1049
  for (var j = 0; j < vranges.length; j += 2) {
1056
1050
  var rtop = vranges[j], rbot = vranges[j+1];
1057
1051
  if (rtop > bot || rbot < top) continue;
@@ -1060,19 +1054,38 @@ window.CodeMirror = (function() {
1060
1054
  Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1061
1055
  vranges[j] = Math.min(top, rtop);
1062
1056
  vranges[j+1] = Math.max(bot, rbot);
1063
- break;
1057
+ return j;
1058
+ }
1059
+ }
1060
+ vranges.push(top, bot);
1061
+ return j;
1062
+ }
1063
+
1064
+ for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1065
+ var size, node = cur;
1066
+ // A widget might wrap, needs special care
1067
+ if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
1068
+ if (cur.firstChild.nodeType == 1) node = cur.firstChild;
1069
+ var rects = node.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1];
1070
+ if (rects.length > 1) {
1071
+ var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top);
1072
+ var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top);
1073
+ data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left,
1074
+ top: vCatLeft, topRight: vCatRight};
1075
+ continue;
1064
1076
  }
1065
1077
  }
1066
- if (j == vranges.length) vranges.push(top, bot);
1078
+ size = getRect(node);
1079
+ var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top);
1067
1080
  var right = size.right;
1068
1081
  if (cur.measureRight) right = getRect(cur.measureRight).left;
1069
- data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
1082
+ data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat};
1070
1083
  }
1071
1084
  for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1072
- var vr = cur.top;
1085
+ var vr = cur.top, vrRight = cur.topRight;
1073
1086
  cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1087
+ if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; }
1074
1088
  }
1075
-
1076
1089
  return data;
1077
1090
  }
1078
1091
 
@@ -1083,7 +1096,7 @@ window.CodeMirror = (function() {
1083
1096
  if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1084
1097
  }
1085
1098
  var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1086
- if (cached) return measureChar(cm, line, line.text.length, cached.measure).right;
1099
+ if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
1087
1100
 
1088
1101
  var pre = lineContent(cm, line);
1089
1102
  var end = pre.appendChild(zeroWidthElement(cm.display.measure));
@@ -1098,6 +1111,9 @@ window.CodeMirror = (function() {
1098
1111
  cm.display.lineNumChars = null;
1099
1112
  }
1100
1113
 
1114
+ function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
1115
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
1116
+
1101
1117
  // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1102
1118
  function intoCoordSystem(cm, lineObj, rect, context) {
1103
1119
  if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
@@ -1107,11 +1123,12 @@ window.CodeMirror = (function() {
1107
1123
  if (context == "line") return rect;
1108
1124
  if (!context) context = "local";
1109
1125
  var yOff = heightAtLine(cm, lineObj);
1110
- if (context != "local") yOff -= cm.display.viewOffset;
1111
- if (context == "page") {
1126
+ if (context == "local") yOff += paddingTop(cm.display);
1127
+ else yOff -= cm.display.viewOffset;
1128
+ if (context == "page" || context == "window") {
1112
1129
  var lOff = getRect(cm.display.lineSpace);
1113
- yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1114
- var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1130
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
1131
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
1115
1132
  rect.left += xOff; rect.right += xOff;
1116
1133
  }
1117
1134
  rect.top += yOff; rect.bottom += yOff;
@@ -1123,31 +1140,30 @@ window.CodeMirror = (function() {
1123
1140
  function fromCoordSystem(cm, coords, context) {
1124
1141
  if (context == "div") return coords;
1125
1142
  var left = coords.left, top = coords.top;
1143
+ // First move into "page" coordinate system
1126
1144
  if (context == "page") {
1127
- left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
1128
- top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
1145
+ left -= pageScrollX();
1146
+ top -= pageScrollY();
1147
+ } else if (context == "local" || !context) {
1148
+ var localBox = getRect(cm.display.sizer);
1149
+ left += localBox.left;
1150
+ top += localBox.top;
1129
1151
  }
1152
+
1130
1153
  var lineSpaceBox = getRect(cm.display.lineSpace);
1131
- left -= lineSpaceBox.left;
1132
- top -= lineSpaceBox.top;
1133
- if (context == "local" || !context) {
1134
- var editorBox = getRect(cm.display.wrapper);
1135
- left += editorBox.left;
1136
- top += editorBox.top;
1137
- }
1138
- return {left: left, top: top};
1154
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
1139
1155
  }
1140
1156
 
1141
- function charCoords(cm, pos, context, lineObj) {
1157
+ function charCoords(cm, pos, context, lineObj, bias) {
1142
1158
  if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1143
- return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1159
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
1144
1160
  }
1145
1161
 
1146
1162
  function cursorCoords(cm, pos, context, lineObj, measurement) {
1147
1163
  lineObj = lineObj || getLine(cm.doc, pos.line);
1148
1164
  if (!measurement) measurement = measureLine(cm, lineObj);
1149
1165
  function get(ch, right) {
1150
- var m = measureChar(cm, lineObj, ch, measurement);
1166
+ var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
1151
1167
  if (right) m.left = m.right; else m.right = m.left;
1152
1168
  return intoCoordSystem(cm, lineObj, m, context);
1153
1169
  }
@@ -1173,8 +1189,9 @@ window.CodeMirror = (function() {
1173
1189
  return val;
1174
1190
  }
1175
1191
 
1176
- function PosMaybeOutside(line, ch, outside) {
1192
+ function PosWithInfo(line, ch, outside, xRel) {
1177
1193
  var pos = new Pos(line, ch);
1194
+ pos.xRel = xRel;
1178
1195
  if (outside) pos.outside = true;
1179
1196
  return pos;
1180
1197
  }
@@ -1183,10 +1200,10 @@ window.CodeMirror = (function() {
1183
1200
  function coordsChar(cm, x, y) {
1184
1201
  var doc = cm.doc;
1185
1202
  y += cm.display.viewOffset;
1186
- if (y < 0) return PosMaybeOutside(doc.first, 0, true);
1203
+ if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
1187
1204
  var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1188
1205
  if (lineNo > last)
1189
- return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
1206
+ return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
1190
1207
  if (x < 0) x = 0;
1191
1208
 
1192
1209
  for (;;) {
@@ -1194,7 +1211,7 @@ window.CodeMirror = (function() {
1194
1211
  var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1195
1212
  var merged = collapsedSpanAtEnd(lineObj);
1196
1213
  var mergedPos = merged && merged.find();
1197
- if (merged && found.ch >= mergedPos.from.ch)
1214
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
1198
1215
  lineNo = mergedPos.to.line;
1199
1216
  else
1200
1217
  return found;
@@ -1220,14 +1237,15 @@ window.CodeMirror = (function() {
1220
1237
  var from = lineLeft(lineObj), to = lineRight(lineObj);
1221
1238
  var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1222
1239
 
1223
- if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
1240
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
1224
1241
  // Do a binary search between these bounds.
1225
1242
  for (;;) {
1226
1243
  if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1227
- var after = x - fromX < toX - x, ch = after ? from : to;
1244
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
1245
+ var xDiff = x - (ch == from ? fromX : toX);
1228
1246
  while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1229
- var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
1230
- pos.after = after;
1247
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
1248
+ xDiff < 0 ? -1 : xDiff ? 1 : 0);
1231
1249
  return pos;
1232
1250
  }
1233
1251
  var step = Math.ceil(dist / 2), middle = from + step;
@@ -1413,7 +1431,7 @@ window.CodeMirror = (function() {
1413
1431
  // supported or compatible enough yet to rely on.)
1414
1432
  function readInput(cm) {
1415
1433
  var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1416
- if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
1434
+ if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
1417
1435
  var text = input.value;
1418
1436
  if (text == prevInput && posEq(sel.from, sel.to)) return false;
1419
1437
  if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
@@ -1431,11 +1449,14 @@ window.CodeMirror = (function() {
1431
1449
  from = Pos(from.line, from.ch - (prevInput.length - same));
1432
1450
  else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1433
1451
  to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1434
- var updateInput = cm.curOp.updateInput;
1435
- makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
1436
- origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
1437
1452
 
1453
+ var updateInput = cm.curOp.updateInput;
1454
+ var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
1455
+ origin: cm.state.pasteIncoming ? "paste" : "+input"};
1456
+ makeChange(cm.doc, changeEvent, "end");
1438
1457
  cm.curOp.updateInput = updateInput;
1458
+ signalLater(cm, "inputRead", cm, changeEvent);
1459
+
1439
1460
  if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1440
1461
  else cm.display.prevInput = text;
1441
1462
  if (withOp) endOperation(cm);
@@ -1476,6 +1497,7 @@ window.CodeMirror = (function() {
1476
1497
  on(d.scroller, "mousedown", operation(cm, onMouseDown));
1477
1498
  if (ie)
1478
1499
  on(d.scroller, "dblclick", operation(cm, function(e) {
1500
+ if (signalDOMEvent(cm, e)) return;
1479
1501
  var pos = posFromMouse(cm, e);
1480
1502
  if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
1481
1503
  e_preventDefault(e);
@@ -1483,7 +1505,7 @@ window.CodeMirror = (function() {
1483
1505
  extendSelection(cm.doc, word.from, word.to);
1484
1506
  }));
1485
1507
  else
1486
- on(d.scroller, "dblclick", e_preventDefault);
1508
+ on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
1487
1509
  on(d.lineSpace, "selectstart", function(e) {
1488
1510
  if (!eventInWidget(d, e)) e_preventDefault(e);
1489
1511
  });
@@ -1515,11 +1537,15 @@ window.CodeMirror = (function() {
1515
1537
  // Prevent wrapper from ever scrolling
1516
1538
  on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1517
1539
 
1540
+ var resizeTimer;
1518
1541
  function onResize() {
1519
- // Might be a text scaling operation, clear size caches.
1520
- d.cachedCharWidth = d.cachedTextHeight = null;
1521
- clearCaches(cm);
1522
- runInOp(cm, bind(regChange, cm));
1542
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
1543
+ resizeTimer = null;
1544
+ // Might be a text scaling operation, clear size caches.
1545
+ d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
1546
+ clearCaches(cm);
1547
+ runInOp(cm, bind(regChange, cm));
1548
+ }, 100);
1523
1549
  }
1524
1550
  on(window, "resize", onResize);
1525
1551
  // Above handler holds on to the editor and its data structures.
@@ -1533,7 +1559,7 @@ window.CodeMirror = (function() {
1533
1559
  setTimeout(unregister, 5000);
1534
1560
 
1535
1561
  on(d.input, "keyup", operation(cm, function(e) {
1536
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1562
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1537
1563
  if (e.keyCode == 16) cm.doc.sel.shift = false;
1538
1564
  }));
1539
1565
  on(d.input, "input", bind(fastPoll, cm));
@@ -1543,7 +1569,7 @@ window.CodeMirror = (function() {
1543
1569
  on(d.input, "blur", bind(onBlur, cm));
1544
1570
 
1545
1571
  function drag_(e) {
1546
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1572
+ if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1547
1573
  e_stop(e);
1548
1574
  }
1549
1575
  if (cm.options.dragDrop) {
@@ -1582,9 +1608,7 @@ window.CodeMirror = (function() {
1582
1608
 
1583
1609
  function eventInWidget(display, e) {
1584
1610
  for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1585
- if (!n) return true;
1586
- if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1587
- n.parentNode == display.sizer && n != display.mover) return true;
1611
+ if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
1588
1612
  }
1589
1613
  }
1590
1614
 
@@ -1604,6 +1628,7 @@ window.CodeMirror = (function() {
1604
1628
 
1605
1629
  var lastClick, lastDoubleClick;
1606
1630
  function onMouseDown(e) {
1631
+ if (signalDOMEvent(this, e)) return;
1607
1632
  var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1608
1633
  sel.shift = e.shiftKey;
1609
1634
 
@@ -1727,8 +1752,6 @@ window.CodeMirror = (function() {
1727
1752
 
1728
1753
  function done(e) {
1729
1754
  counter = Infinity;
1730
- var cur = posFromMouse(cm, e);
1731
- if (cur) doSelect(cur);
1732
1755
  e_preventDefault(e);
1733
1756
  focusInput(cm);
1734
1757
  off(document, "mousemove", move);
@@ -1744,11 +1767,41 @@ window.CodeMirror = (function() {
1744
1767
  on(document, "mouseup", up);
1745
1768
  }
1746
1769
 
1770
+ function clickInGutter(cm, e) {
1771
+ var display = cm.display;
1772
+ try { var mX = e.clientX, mY = e.clientY; }
1773
+ catch(e) { return false; }
1774
+
1775
+ if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1776
+ e_preventDefault(e);
1777
+ if (!hasHandler(cm, "gutterClick")) return true;
1778
+
1779
+ var lineBox = getRect(display.lineDiv);
1780
+ if (mY > lineBox.bottom) return true;
1781
+ mY -= lineBox.top - display.viewOffset;
1782
+
1783
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
1784
+ var g = display.gutters.childNodes[i];
1785
+ if (g && getRect(g).right >= mX) {
1786
+ var line = lineAtHeight(cm.doc, mY);
1787
+ var gutter = cm.options.gutters[i];
1788
+ signalLater(cm, "gutterClick", cm, line, gutter, e);
1789
+ break;
1790
+ }
1791
+ }
1792
+ return true;
1793
+ }
1794
+
1795
+ // Kludge to work around strange IE behavior where it'll sometimes
1796
+ // re-fire a series of drag-related events right after the drop (#1551)
1797
+ var lastDrop = 0;
1798
+
1747
1799
  function onDrop(e) {
1748
1800
  var cm = this;
1749
- if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1801
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1750
1802
  return;
1751
1803
  e_preventDefault(e);
1804
+ if (ie) lastDrop = +new Date;
1752
1805
  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1753
1806
  if (!pos || isReadOnly(cm)) return;
1754
1807
  if (files && files.length && window.FileReader && window.File) {
@@ -1788,41 +1841,16 @@ window.CodeMirror = (function() {
1788
1841
  }
1789
1842
  }
1790
1843
 
1791
- function clickInGutter(cm, e) {
1792
- var display = cm.display;
1793
- try { var mX = e.clientX, mY = e.clientY; }
1794
- catch(e) { return false; }
1795
-
1796
- if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1797
- e_preventDefault(e);
1798
- if (!hasHandler(cm, "gutterClick")) return true;
1799
-
1800
- var lineBox = getRect(display.lineDiv);
1801
- if (mY > lineBox.bottom) return true;
1802
- mY -= lineBox.top - display.viewOffset;
1803
-
1804
- for (var i = 0; i < cm.options.gutters.length; ++i) {
1805
- var g = display.gutters.childNodes[i];
1806
- if (g && getRect(g).right >= mX) {
1807
- var line = lineAtHeight(cm.doc, mY);
1808
- var gutter = cm.options.gutters[i];
1809
- signalLater(cm, "gutterClick", cm, line, gutter, e);
1810
- break;
1811
- }
1812
- }
1813
- return true;
1814
- }
1815
-
1816
1844
  function onDragStart(cm, e) {
1817
- if (ie && !cm.state.draggingText) { e_stop(e); return; }
1818
- if (eventInWidget(cm.display, e)) return;
1845
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
1846
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
1819
1847
 
1820
1848
  var txt = cm.getSelection();
1821
1849
  e.dataTransfer.setData("Text", txt);
1822
1850
 
1823
1851
  // Use dummy image instead of default browsers image.
1824
1852
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1825
- if (e.dataTransfer.setDragImage) {
1853
+ if (e.dataTransfer.setDragImage && !safari) {
1826
1854
  var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1827
1855
  if (opera) {
1828
1856
  img.width = img.height = 1;
@@ -1830,15 +1858,6 @@ window.CodeMirror = (function() {
1830
1858
  // Force a relayout, or Opera won't use our image for some obscure reason
1831
1859
  img._top = img.offsetTop;
1832
1860
  }
1833
- if (safari) {
1834
- if (cm.display.dragImg) {
1835
- img = cm.display.dragImg;
1836
- } else {
1837
- cm.display.dragImg = img;
1838
- img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1839
- cm.display.wrapper.appendChild(img);
1840
- }
1841
- }
1842
1861
  e.dataTransfer.setDragImage(img, 0, 0);
1843
1862
  if (opera) img.parentNode.removeChild(img);
1844
1863
  }
@@ -1851,6 +1870,7 @@ window.CodeMirror = (function() {
1851
1870
  if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1852
1871
  if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1853
1872
  if (gecko) updateDisplay(cm, []);
1873
+ startWorker(cm, 100);
1854
1874
  }
1855
1875
  function setScrollLeft(cm, val, isScroller) {
1856
1876
  if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
@@ -1983,8 +2003,10 @@ window.CodeMirror = (function() {
1983
2003
  var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1984
2004
  clearTimeout(maybeTransition);
1985
2005
  if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1986
- if (getKeyMap(cm.options.keyMap) == startMap)
2006
+ if (getKeyMap(cm.options.keyMap) == startMap) {
1987
2007
  cm.options.keyMap = (next.call ? next.call(null, cm) : next);
2008
+ keyMapChanged(cm);
2009
+ }
1988
2010
  }, 50);
1989
2011
 
1990
2012
  var name = keyName(e, true), handled = false;
@@ -1997,17 +2019,18 @@ window.CodeMirror = (function() {
1997
2019
  // 'go') bound to the keyname without 'Shift-'.
1998
2020
  handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
1999
2021
  || lookupKey(name, keymaps, function(b) {
2000
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
2022
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
2023
+ return doHandleBinding(cm, b);
2001
2024
  });
2002
2025
  } else {
2003
2026
  handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
2004
2027
  }
2005
- if (handled == "stop") handled = false;
2006
2028
 
2007
2029
  if (handled) {
2008
2030
  e_preventDefault(e);
2009
2031
  restartBlink(cm);
2010
2032
  if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
2033
+ signalLater(cm, "keyHandled", cm, name, e);
2011
2034
  }
2012
2035
  return handled;
2013
2036
  }
@@ -2018,6 +2041,7 @@ window.CodeMirror = (function() {
2018
2041
  if (handled) {
2019
2042
  e_preventDefault(e);
2020
2043
  restartBlink(cm);
2044
+ signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
2021
2045
  }
2022
2046
  return handled;
2023
2047
  }
@@ -2027,7 +2051,7 @@ window.CodeMirror = (function() {
2027
2051
  var cm = this;
2028
2052
  if (!cm.state.focused) onFocus(cm);
2029
2053
  if (ie && e.keyCode == 27) { e.returnValue = false; }
2030
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2054
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2031
2055
  var code = e.keyCode;
2032
2056
  // IE does strange things with escape.
2033
2057
  cm.doc.sel.shift = code == 16 || e.shiftKey;
@@ -2043,7 +2067,7 @@ window.CodeMirror = (function() {
2043
2067
 
2044
2068
  function onKeyPress(e) {
2045
2069
  var cm = this;
2046
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2070
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2047
2071
  var keyCode = e.keyCode, charCode = e.charCode;
2048
2072
  if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2049
2073
  if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
@@ -2141,11 +2165,11 @@ window.CodeMirror = (function() {
2141
2165
 
2142
2166
  // UPDATING
2143
2167
 
2144
- function changeEnd(change) {
2168
+ var changeEnd = CodeMirror.changeEnd = function(change) {
2145
2169
  if (!change.text) return change.to;
2146
2170
  return Pos(change.from.line + change.text.length - 1,
2147
2171
  lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2148
- }
2172
+ };
2149
2173
 
2150
2174
  // Make sure a position will be valid after the given change.
2151
2175
  function clipPostChange(doc, change, pos) {
@@ -2187,21 +2211,21 @@ window.CodeMirror = (function() {
2187
2211
  return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2188
2212
  }
2189
2213
 
2190
- function filterChange(doc, change) {
2214
+ function filterChange(doc, change, update) {
2191
2215
  var obj = {
2192
2216
  canceled: false,
2193
2217
  from: change.from,
2194
2218
  to: change.to,
2195
2219
  text: change.text,
2196
2220
  origin: change.origin,
2197
- update: function(from, to, text, origin) {
2198
- if (from) this.from = clipPos(doc, from);
2199
- if (to) this.to = clipPos(doc, to);
2200
- if (text) this.text = text;
2201
- if (origin !== undefined) this.origin = origin;
2202
- },
2203
2221
  cancel: function() { this.canceled = true; }
2204
2222
  };
2223
+ if (update) obj.update = function(from, to, text, origin) {
2224
+ if (from) this.from = clipPos(doc, from);
2225
+ if (to) this.to = clipPos(doc, to);
2226
+ if (text) this.text = text;
2227
+ if (origin !== undefined) this.origin = origin;
2228
+ };
2205
2229
  signal(doc, "beforeChange", doc, obj);
2206
2230
  if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2207
2231
 
@@ -2218,7 +2242,7 @@ window.CodeMirror = (function() {
2218
2242
  }
2219
2243
 
2220
2244
  if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2221
- change = filterChange(doc, change);
2245
+ change = filterChange(doc, change, true);
2222
2246
  if (!change) return;
2223
2247
  }
2224
2248
 
@@ -2257,15 +2281,23 @@ window.CodeMirror = (function() {
2257
2281
  var hist = doc.history;
2258
2282
  var event = (type == "undo" ? hist.done : hist.undone).pop();
2259
2283
  if (!event) return;
2260
- hist.dirtyCounter += type == "undo" ? -1 : 1;
2261
2284
 
2262
2285
  var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2263
- anchorAfter: event.anchorBefore, headAfter: event.headBefore};
2286
+ anchorAfter: event.anchorBefore, headAfter: event.headBefore,
2287
+ generation: hist.generation};
2264
2288
  (type == "undo" ? hist.undone : hist.done).push(anti);
2289
+ hist.generation = event.generation || ++hist.maxGeneration;
2290
+
2291
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
2265
2292
 
2266
2293
  for (var i = event.changes.length - 1; i >= 0; --i) {
2267
2294
  var change = event.changes[i];
2268
2295
  change.origin = type;
2296
+ if (filter && !filterChange(doc, change, false)) {
2297
+ (type == "undo" ? hist.done : hist.undone).length = 0;
2298
+ return;
2299
+ }
2300
+
2269
2301
  anti.changes.push(historyChangeFromChange(doc, change));
2270
2302
 
2271
2303
  var after = i ? computeSelAfterChange(doc, change, null)
@@ -2529,9 +2561,9 @@ window.CodeMirror = (function() {
2529
2561
  function scrollCursorIntoView(cm) {
2530
2562
  var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
2531
2563
  if (!cm.state.focused) return;
2532
- var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
2533
- if (coords.top + pTop + box.top < 0) doScroll = true;
2534
- else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2564
+ var display = cm.display, box = getRect(display.sizer), doScroll = null;
2565
+ if (coords.top + box.top < 0) doScroll = true;
2566
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2535
2567
  if (doScroll != null && !phantom) {
2536
2568
  var hidden = display.cursor.style.display == "none";
2537
2569
  if (hidden) {
@@ -2569,12 +2601,11 @@ window.CodeMirror = (function() {
2569
2601
  }
2570
2602
 
2571
2603
  function calculateScrollPos(cm, x1, y1, x2, y2) {
2572
- var display = cm.display, pt = paddingTop(display);
2573
- y1 += pt; y2 += pt;
2604
+ var display = cm.display, snapMargin = textHeight(cm.display);
2574
2605
  if (y1 < 0) y1 = 0;
2575
2606
  var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2576
2607
  var docBottom = cm.doc.height + paddingVert(display);
2577
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2608
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
2578
2609
  if (y1 < screentop) {
2579
2610
  result.scrollTop = atTop ? 0 : y1;
2580
2611
  } else if (y2 > screentop + screen) {
@@ -2611,7 +2642,7 @@ window.CodeMirror = (function() {
2611
2642
 
2612
2643
  function indentLine(cm, n, how, aggressive) {
2613
2644
  var doc = cm.doc;
2614
- if (!how) how = "add";
2645
+ if (how == null) how = "add";
2615
2646
  if (how == "smart") {
2616
2647
  if (!cm.doc.mode.indent) how = "prev";
2617
2648
  else var state = getStateBefore(cm, n);
@@ -2634,6 +2665,8 @@ window.CodeMirror = (function() {
2634
2665
  indentation = curSpace + cm.options.indentUnit;
2635
2666
  } else if (how == "subtract") {
2636
2667
  indentation = curSpace - cm.options.indentUnit;
2668
+ } else if (typeof how == "number") {
2669
+ indentation = curSpace + how;
2637
2670
  }
2638
2671
  indentation = Math.max(0, indentation);
2639
2672
 
@@ -2722,7 +2755,7 @@ window.CodeMirror = (function() {
2722
2755
  function findWordAt(line, pos) {
2723
2756
  var start = pos.ch, end = pos.ch;
2724
2757
  if (line) {
2725
- if (pos.after === false || end == line.length) --start; else ++end;
2758
+ if (pos.xRel < 0 || end == line.length) --start; else ++end;
2726
2759
  var startChar = line.charAt(start);
2727
2760
  var check = isWordChar(startChar) ? isWordChar
2728
2761
  : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
@@ -2779,7 +2812,8 @@ window.CodeMirror = (function() {
2779
2812
  removeOverlay: operation(null, function(spec) {
2780
2813
  var overlays = this.state.overlays;
2781
2814
  for (var i = 0; i < overlays.length; ++i) {
2782
- if (overlays[i].modeSpec == spec) {
2815
+ var cur = overlays[i].modeSpec;
2816
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
2783
2817
  overlays.splice(i, 1);
2784
2818
  this.state.modeGen++;
2785
2819
  regChange(this);
@@ -2789,7 +2823,7 @@ window.CodeMirror = (function() {
2789
2823
  }),
2790
2824
 
2791
2825
  indentLine: operation(null, function(n, dir, aggressive) {
2792
- if (typeof dir != "string") {
2826
+ if (typeof dir != "string" && typeof dir != "number") {
2793
2827
  if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2794
2828
  else dir = dir ? "add" : "subtract";
2795
2829
  }
@@ -2804,10 +2838,10 @@ window.CodeMirror = (function() {
2804
2838
 
2805
2839
  // Fetch the parser token for a given character. Useful for hacks
2806
2840
  // that want to inspect the mode state (say, for completion).
2807
- getTokenAt: function(pos) {
2841
+ getTokenAt: function(pos, precise) {
2808
2842
  var doc = this.doc;
2809
2843
  pos = clipPos(doc, pos);
2810
- var state = getStateBefore(this, pos.line), mode = this.doc.mode;
2844
+ var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
2811
2845
  var line = getLine(doc, pos.line);
2812
2846
  var stream = new StringStream(line.text, this.options.tabSize);
2813
2847
  while (stream.pos < pos.ch && !stream.eol()) {
@@ -2822,10 +2856,22 @@ window.CodeMirror = (function() {
2822
2856
  state: state};
2823
2857
  },
2824
2858
 
2825
- getStateAfter: function(line) {
2859
+ getTokenTypeAt: function(pos) {
2860
+ pos = clipPos(this.doc, pos);
2861
+ var styles = getLineStyles(this, getLine(this.doc, pos.line));
2862
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
2863
+ for (;;) {
2864
+ var mid = (before + after) >> 1;
2865
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
2866
+ else if (styles[mid * 2 + 1] < ch) before = mid + 1;
2867
+ else return styles[mid * 2 + 2];
2868
+ }
2869
+ },
2870
+
2871
+ getStateAfter: function(line, precise) {
2826
2872
  var doc = this.doc;
2827
2873
  line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2828
- return getStateBefore(this, line + 1);
2874
+ return getStateBefore(this, line + 1, precise);
2829
2875
  },
2830
2876
 
2831
2877
  cursorCoords: function(start, mode) {
@@ -2845,6 +2891,19 @@ window.CodeMirror = (function() {
2845
2891
  return coordsChar(this, coords.left, coords.top);
2846
2892
  },
2847
2893
 
2894
+ lineAtHeight: function(height, mode) {
2895
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
2896
+ return lineAtHeight(this.doc, height + this.display.viewOffset);
2897
+ },
2898
+ heightAtLine: function(line, mode) {
2899
+ var end = false, last = this.doc.first + this.doc.size - 1;
2900
+ if (line < this.doc.first) line = this.doc.first;
2901
+ else if (line > last) { line = last; end = true; }
2902
+ var lineObj = getLine(this.doc, line);
2903
+ return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
2904
+ (end ? lineObj.height : 0);
2905
+ },
2906
+
2848
2907
  defaultTextHeight: function() { return textHeight(this.display); },
2849
2908
  defaultCharWidth: function() { return charWidth(this.display); },
2850
2909
 
@@ -2873,7 +2932,7 @@ window.CodeMirror = (function() {
2873
2932
  return changeLine(this, handle, function(line) {
2874
2933
  var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2875
2934
  if (!line[prop]) line[prop] = cls;
2876
- else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2935
+ else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
2877
2936
  else line[prop] += " " + cls;
2878
2937
  return true;
2879
2938
  });
@@ -2886,9 +2945,10 @@ window.CodeMirror = (function() {
2886
2945
  if (!cur) return false;
2887
2946
  else if (cls == null) line[prop] = null;
2888
2947
  else {
2889
- var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2890
- if (upd == cur) return false;
2891
- line[prop] = upd || null;
2948
+ var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
2949
+ if (!found) return false;
2950
+ var end = found.index + found[0].length;
2951
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
2892
2952
  }
2893
2953
  return true;
2894
2954
  });
@@ -2936,7 +2996,7 @@ window.CodeMirror = (function() {
2936
2996
  if (left + node.offsetWidth > hspace)
2937
2997
  left = hspace - node.offsetWidth;
2938
2998
  }
2939
- node.style.top = (top + paddingTop(display)) + "px";
2999
+ node.style.top = top + "px";
2940
3000
  node.style.left = node.style.right = "";
2941
3001
  if (horiz == "right") {
2942
3002
  left = display.sizer.clientWidth - node.offsetWidth;
@@ -3281,6 +3341,10 @@ window.CodeMirror = (function() {
3281
3341
  var l = cm.getCursor().line;
3282
3342
  cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3283
3343
  },
3344
+ delLineLeft: function(cm) {
3345
+ var cur = cm.getCursor();
3346
+ cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
3347
+ },
3284
3348
  undo: function(cm) {cm.undo();},
3285
3349
  redo: function(cm) {cm.redo();},
3286
3350
  goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
@@ -3376,7 +3440,7 @@ window.CodeMirror = (function() {
3376
3440
  "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3377
3441
  "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3378
3442
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3379
- "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3443
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
3380
3444
  fallthrough: ["basic", "emacsy"]
3381
3445
  };
3382
3446
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -3415,7 +3479,7 @@ window.CodeMirror = (function() {
3415
3479
 
3416
3480
  for (var i = 0; i < maps.length; ++i) {
3417
3481
  var done = lookup(maps[i]);
3418
- if (done) return done;
3482
+ if (done) return done != "stop";
3419
3483
  }
3420
3484
  }
3421
3485
  function isModifierKey(event) {
@@ -3597,7 +3661,7 @@ window.CodeMirror = (function() {
3597
3661
  if (min != null && cm) regChange(cm, min, max + 1);
3598
3662
  this.lines.length = 0;
3599
3663
  this.explicitlyCleared = true;
3600
- if (this.collapsed && this.doc.cantEdit) {
3664
+ if (this.atomic && this.doc.cantEdit) {
3601
3665
  this.doc.cantEdit = false;
3602
3666
  if (cm) reCheckSelection(cm);
3603
3667
  }
@@ -3660,6 +3724,7 @@ window.CodeMirror = (function() {
3660
3724
  if (marker.replacedWith) {
3661
3725
  marker.collapsed = true;
3662
3726
  marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3727
+ if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
3663
3728
  }
3664
3729
  if (marker.collapsed) sawCollapsedSpans = true;
3665
3730
 
@@ -3833,6 +3898,13 @@ window.CodeMirror = (function() {
3833
3898
  }
3834
3899
  }
3835
3900
  }
3901
+ if (sameLine && first) {
3902
+ // Make sure we didn't create any zero-length spans
3903
+ for (var i = 0; i < first.length; ++i)
3904
+ if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
3905
+ first.splice(i--, 1);
3906
+ if (!first.length) first = null;
3907
+ }
3836
3908
 
3837
3909
  var newMarkers = [first];
3838
3910
  if (!sameLine) {
@@ -3927,6 +3999,7 @@ window.CodeMirror = (function() {
3927
3999
  sp = sps[i];
3928
4000
  if (!sp.marker.collapsed) continue;
3929
4001
  if (sp.from == null) return true;
4002
+ if (sp.marker.replacedWith) continue;
3930
4003
  if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3931
4004
  return true;
3932
4005
  }
@@ -3940,7 +4013,7 @@ window.CodeMirror = (function() {
3940
4013
  return true;
3941
4014
  for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3942
4015
  sp = line.markedSpans[i];
3943
- if (sp.marker.collapsed && sp.from == span.to &&
4016
+ if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
3944
4017
  (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3945
4018
  lineIsHiddenInner(doc, line, sp)) return true;
3946
4019
  }
@@ -4052,7 +4125,7 @@ window.CodeMirror = (function() {
4052
4125
  function runMode(cm, text, mode, state, f) {
4053
4126
  var flattenSpans = mode.flattenSpans;
4054
4127
  if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
4055
- var curText = "", curStyle = null;
4128
+ var curStart = 0, curStyle = null;
4056
4129
  var stream = new StringStream(text, cm.options.tabSize), style;
4057
4130
  if (text == "" && mode.blankLine) mode.blankLine(state);
4058
4131
  while (!stream.eol()) {
@@ -4064,14 +4137,13 @@ window.CodeMirror = (function() {
4064
4137
  } else {
4065
4138
  style = mode.token(stream, state);
4066
4139
  }
4067
- var substr = stream.current();
4068
- stream.start = stream.pos;
4069
4140
  if (!flattenSpans || curStyle != style) {
4070
- if (curText) f(curText, curStyle);
4071
- curText = substr; curStyle = style;
4072
- } else curText = curText + substr;
4141
+ if (curStart < stream.start) f(stream.start, curStyle);
4142
+ curStart = stream.start; curStyle = style;
4143
+ }
4144
+ stream.start = stream.pos;
4073
4145
  }
4074
- if (curText) f(curText, curStyle);
4146
+ if (curStart < stream.pos) f(stream.pos, curStyle);
4075
4147
  }
4076
4148
 
4077
4149
  function highlightLine(cm, line, state) {
@@ -4079,27 +4151,24 @@ window.CodeMirror = (function() {
4079
4151
  // mode/overlays that it is based on (for easy invalidation).
4080
4152
  var st = [cm.state.modeGen];
4081
4153
  // Compute the base array of styles
4082
- runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
4154
+ runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
4083
4155
 
4084
4156
  // Run overlays, adjust style array.
4085
4157
  for (var o = 0; o < cm.state.overlays.length; ++o) {
4086
- var overlay = cm.state.overlays[o], i = 1;
4087
- runMode(cm, line.text, overlay.mode, true, function(txt, style) {
4088
- var start = i, len = txt.length;
4158
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
4159
+ runMode(cm, line.text, overlay.mode, true, function(end, style) {
4160
+ var start = i;
4089
4161
  // Ensure there's a token end at the current position, and that i points at it
4090
- while (len) {
4091
- var cur = st[i], len_ = cur.length;
4092
- if (len_ <= len) {
4093
- len -= len_;
4094
- } else {
4095
- st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
4096
- len = 0;
4097
- }
4162
+ while (at < end) {
4163
+ var i_end = st[i];
4164
+ if (i_end > end)
4165
+ st.splice(i, 1, end, st[i+1], i_end);
4098
4166
  i += 2;
4167
+ at = Math.min(end, i_end);
4099
4168
  }
4100
4169
  if (!style) return;
4101
4170
  if (overlay.opaque) {
4102
- st.splice(start, i - start, txt, style);
4171
+ st.splice(start, i - start, end, style);
4103
4172
  i = start + 2;
4104
4173
  } else {
4105
4174
  for (; start < i; start += 2) {
@@ -4139,37 +4208,31 @@ window.CodeMirror = (function() {
4139
4208
  }
4140
4209
 
4141
4210
  function lineContent(cm, realLine, measure) {
4142
- var merged, line = realLine, lineBefore, sawBefore, simple = true;
4143
- while (merged = collapsedSpanAtStart(line)) {
4144
- simple = false;
4211
+ var merged, line = realLine, empty = true;
4212
+ while (merged = collapsedSpanAtStart(line))
4145
4213
  line = getLine(cm.doc, merged.find().from.line);
4146
- if (!lineBefore) lineBefore = line;
4147
- }
4148
4214
 
4149
4215
  var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
4150
- measure: null, addedOne: false, cm: cm};
4216
+ measure: null, measuredSomething: false, cm: cm};
4151
4217
  if (line.textClass) builder.pre.className = line.textClass;
4152
4218
 
4153
4219
  do {
4220
+ if (line.text) empty = false;
4154
4221
  builder.measure = line == realLine && measure;
4155
4222
  builder.pos = 0;
4156
4223
  builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4157
4224
  if ((ie || webkit) && cm.getOption("lineWrapping"))
4158
4225
  builder.addToken = buildTokenSplitSpaces(builder.addToken);
4159
- if (measure && sawBefore && line != realLine && !builder.addedOne) {
4160
- measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4161
- builder.addedOne = true;
4162
- }
4163
4226
  var next = insertLineContent(line, builder, getLineStyles(cm, line));
4164
- sawBefore = line == lineBefore;
4165
- if (next) {
4166
- line = getLine(cm.doc, next.to.line);
4167
- simple = false;
4227
+ if (measure && line == realLine && !builder.measuredSomething) {
4228
+ measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4229
+ builder.measuredSomething = true;
4168
4230
  }
4231
+ if (next) line = getLine(cm.doc, next.to.line);
4169
4232
  } while (next);
4170
4233
 
4171
- if (measure && !builder.addedOne)
4172
- measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4234
+ if (measure && !builder.measuredSomething && !measure[0])
4235
+ measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4173
4236
  if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4174
4237
  builder.pre.appendChild(document.createTextNode("\u00a0"));
4175
4238
 
@@ -4252,7 +4315,7 @@ window.CodeMirror = (function() {
4252
4315
  span.style.whiteSpace = "normal";
4253
4316
  builder.pos += ch.length;
4254
4317
  }
4255
- if (text.length) builder.addedOne = true;
4318
+ if (text.length) builder.measuredSomething = true;
4256
4319
  }
4257
4320
 
4258
4321
  function buildTokenSplitSpaces(inner) {
@@ -4270,11 +4333,12 @@ window.CodeMirror = (function() {
4270
4333
  function buildCollapsedSpan(builder, size, widget) {
4271
4334
  if (widget) {
4272
4335
  if (!builder.display) widget = widget.cloneNode(true);
4273
- builder.pre.appendChild(widget);
4274
- if (builder.measure && size) {
4275
- builder.measure[builder.pos] = widget;
4276
- builder.addedOne = true;
4336
+ if (builder.measure) {
4337
+ builder.measure[builder.pos] = size ? widget
4338
+ : builder.pre.appendChild(zeroWidthElement(builder.cm.display.measure));
4339
+ builder.measuredSomething = true;
4277
4340
  }
4341
+ builder.pre.appendChild(widget);
4278
4342
  }
4279
4343
  builder.pos += size;
4280
4344
  }
@@ -4282,15 +4346,14 @@ window.CodeMirror = (function() {
4282
4346
  // Outputs a number of spans to make up a line, taking highlighting
4283
4347
  // and marked text into account.
4284
4348
  function insertLineContent(line, builder, styles) {
4285
- var spans = line.markedSpans;
4349
+ var spans = line.markedSpans, allText = line.text, at = 0;
4286
4350
  if (!spans) {
4287
4351
  for (var i = 1; i < styles.length; i+=2)
4288
- builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
4352
+ builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
4289
4353
  return;
4290
4354
  }
4291
4355
 
4292
- var allText = line.text, len = allText.length;
4293
- var pos = 0, i = 1, text = "", style;
4356
+ var len = allText.length, pos = 0, i = 1, text = "", style;
4294
4357
  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4295
4358
  for (;;) {
4296
4359
  if (nextChange == pos) { // Update current marker set
@@ -4304,7 +4367,7 @@ window.CodeMirror = (function() {
4304
4367
  if (m.className) spanStyle += " " + m.className;
4305
4368
  if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4306
4369
  if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4307
- if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
4370
+ if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
4308
4371
  collapsed = sp;
4309
4372
  } else if (sp.from > pos && nextChange > sp.from) {
4310
4373
  nextChange = sp.from;
@@ -4334,7 +4397,8 @@ window.CodeMirror = (function() {
4334
4397
  pos = end;
4335
4398
  spanStartStyle = "";
4336
4399
  }
4337
- text = styles[i++]; style = styleToClass(styles[i++]);
4400
+ text = allText.slice(at, at = styles[i++]);
4401
+ style = styleToClass(styles[i++]);
4338
4402
  }
4339
4403
  }
4340
4404
  }
@@ -4526,6 +4590,7 @@ window.CodeMirror = (function() {
4526
4590
  this.scrollTop = this.scrollLeft = 0;
4527
4591
  this.cantEdit = false;
4528
4592
  this.history = makeHistory();
4593
+ this.cleanGeneration = 1;
4529
4594
  this.frontier = firstLine;
4530
4595
  var start = Pos(firstLine, 0);
4531
4596
  this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
@@ -4626,20 +4691,25 @@ window.CodeMirror = (function() {
4626
4691
  var hist = this.history;
4627
4692
  return {undo: hist.done.length, redo: hist.undone.length};
4628
4693
  },
4629
- clearHistory: function() {this.history = makeHistory();},
4694
+ clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
4630
4695
 
4631
4696
  markClean: function() {
4632
- this.history.dirtyCounter = 0;
4697
+ this.cleanGeneration = this.changeGeneration();
4698
+ },
4699
+ changeGeneration: function() {
4633
4700
  this.history.lastOp = this.history.lastOrigin = null;
4701
+ return this.history.generation;
4702
+ },
4703
+ isClean: function (gen) {
4704
+ return this.history.generation == (gen || this.cleanGeneration);
4634
4705
  },
4635
- isClean: function () {return this.history.dirtyCounter == 0;},
4636
4706
 
4637
4707
  getHistory: function() {
4638
4708
  return {done: copyHistoryArray(this.history.done),
4639
4709
  undone: copyHistoryArray(this.history.undone)};
4640
4710
  },
4641
4711
  setHistory: function(histData) {
4642
- var hist = this.history = makeHistory();
4712
+ var hist = this.history = makeHistory(this.history.maxGeneration);
4643
4713
  hist.done = histData.done.slice(0);
4644
4714
  hist.undone = histData.undone.slice(0);
4645
4715
  },
@@ -4869,7 +4939,7 @@ window.CodeMirror = (function() {
4869
4939
 
4870
4940
  // HISTORY
4871
4941
 
4872
- function makeHistory() {
4942
+ function makeHistory(startGen) {
4873
4943
  return {
4874
4944
  // Arrays of history events. Doing something adds an event to
4875
4945
  // done and clears undo. Undoing moves events from done to
@@ -4879,7 +4949,7 @@ window.CodeMirror = (function() {
4879
4949
  // event
4880
4950
  lastTime: 0, lastOp: null, lastOrigin: null,
4881
4951
  // Used by the isClean() method
4882
- dirtyCounter: 0
4952
+ generation: startGen || 1, maxGeneration: startGen || 1
4883
4953
  };
4884
4954
  }
4885
4955
 
@@ -4923,17 +4993,13 @@ window.CodeMirror = (function() {
4923
4993
  } else {
4924
4994
  // Can not be merged, start a new event.
4925
4995
  cur = {changes: [historyChangeFromChange(doc, change)],
4996
+ generation: hist.generation,
4926
4997
  anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4927
4998
  anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4928
4999
  hist.done.push(cur);
5000
+ hist.generation = ++hist.maxGeneration;
4929
5001
  while (hist.done.length > hist.undoDepth)
4930
5002
  hist.done.shift();
4931
- if (hist.dirtyCounter < 0)
4932
- // The user has made a change after undoing past the last clean state.
4933
- // We can never get back to a clean state now until markClean() is called.
4934
- hist.dirtyCounter = NaN;
4935
- else
4936
- hist.dirtyCounter++;
4937
5003
  }
4938
5004
  hist.lastTime = time;
4939
5005
  hist.lastOp = opId;
@@ -5048,6 +5114,9 @@ window.CodeMirror = (function() {
5048
5114
  if (e.stopPropagation) e.stopPropagation();
5049
5115
  else e.cancelBubble = true;
5050
5116
  }
5117
+ function e_defaultPrevented(e) {
5118
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
5119
+ }
5051
5120
  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
5052
5121
  CodeMirror.e_stop = e_stop;
5053
5122
  CodeMirror.e_preventDefault = e_preventDefault;
@@ -5114,6 +5183,11 @@ window.CodeMirror = (function() {
5114
5183
  delayedCallbacks.push(bnd(arr[i]));
5115
5184
  }
5116
5185
 
5186
+ function signalDOMEvent(cm, e) {
5187
+ signal(cm, e.type, cm, e);
5188
+ return e_defaultPrevented(e);
5189
+ }
5190
+
5117
5191
  function fireDelayed() {
5118
5192
  --delayedCallbackDepth;
5119
5193
  var delayed = delayedCallbacks;
@@ -5168,7 +5242,11 @@ window.CodeMirror = (function() {
5168
5242
  if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5169
5243
  node.selectionStart = 0;
5170
5244
  node.selectionEnd = node.value.length;
5171
- } else node.select();
5245
+ } else {
5246
+ // Suppress mysterious IE10 errors
5247
+ try { node.select(); }
5248
+ catch(_e) {}
5249
+ }
5172
5250
  }
5173
5251
 
5174
5252
  function indexOf(collection, elt) {
@@ -5279,7 +5357,7 @@ window.CodeMirror = (function() {
5279
5357
  spanAffectsWrapping = function(str, i) {
5280
5358
  if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
5281
5359
  return true;
5282
- return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|\?[\w~`@#$%\^&*(_=+{[|><]/.test(str.slice(i - 1, i + 1));
5360
+ return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
5283
5361
  };
5284
5362
 
5285
5363
  var knownScrollbarWidth;
@@ -5631,7 +5709,7 @@ window.CodeMirror = (function() {
5631
5709
 
5632
5710
  // THE END
5633
5711
 
5634
- CodeMirror.version = "3.13";
5712
+ CodeMirror.version = "3.14.0";
5635
5713
 
5636
5714
  return CodeMirror;
5637
5715
  })();