codemirror-rails 3.13 → 3.14

Sign up to get free protection for your applications and to get access to all the features.
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
  })();