codemirror-rails 2.23 → 2.24

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 (29) hide show
  1. data/lib/codemirror/rails/version.rb +2 -2
  2. data/vendor/assets/javascripts/codemirror.js +163 -88
  3. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +1 -1
  4. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +85 -202
  5. data/vendor/assets/javascripts/codemirror/modes/clike.js +1 -1
  6. data/vendor/assets/javascripts/codemirror/modes/gfm.js +1 -1
  7. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +1 -1
  8. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +3 -1
  9. data/vendor/assets/javascripts/codemirror/modes/javascript.js +4 -3
  10. data/vendor/assets/javascripts/codemirror/modes/less.js +27 -13
  11. data/vendor/assets/javascripts/codemirror/modes/markdown.js +1 -1
  12. data/vendor/assets/javascripts/codemirror/modes/php.js +1 -1
  13. data/vendor/assets/javascripts/codemirror/modes/pig.js +172 -0
  14. data/vendor/assets/javascripts/codemirror/modes/python.js +1 -0
  15. data/vendor/assets/javascripts/codemirror/modes/rpm-spec.css +5 -0
  16. data/vendor/assets/javascripts/codemirror/modes/rst.js +1 -1
  17. data/vendor/assets/javascripts/codemirror/modes/shell.js +103 -0
  18. data/vendor/assets/javascripts/codemirror/modes/tiki.js +316 -0
  19. data/vendor/assets/javascripts/codemirror/modes/xml.js +63 -6
  20. data/vendor/assets/javascripts/codemirror/utils/foldcode.js +13 -8
  21. data/vendor/assets/javascripts/codemirror/utils/loadmode.js +50 -0
  22. data/vendor/assets/javascripts/codemirror/utils/overlay.js +1 -1
  23. data/vendor/assets/javascripts/codemirror/utils/search.js +2 -2
  24. data/vendor/assets/stylesheets/codemirror.css +2 -0
  25. data/vendor/assets/stylesheets/codemirror/modes/tiki.css +26 -0
  26. data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +82 -0
  27. data/vendor/assets/stylesheets/codemirror/themes/blackboard.css +25 -0
  28. data/vendor/assets/stylesheets/codemirror/themes/lesser-dark.css +1 -1
  29. metadata +12 -4
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '2.23'
4
- CODEMIRROR_VERSION = '2.23'
3
+ VERSION = '2.24'
4
+ CODEMIRROR_VERSION = '2.24'
5
5
  end
6
6
  end
@@ -1,4 +1,4 @@
1
- // CodeMirror version 2.23
1
+ // CodeMirror version 2.24
2
2
  //
3
3
  // All functions that need access to the editor's state live inside
4
4
  // the CodeMirror function. Below that, at the bottom of the file,
@@ -98,7 +98,6 @@ var CodeMirror = (function() {
98
98
  // Register our event handlers.
99
99
  connect(scroller, "mousedown", operation(onMouseDown));
100
100
  connect(scroller, "dblclick", operation(onDoubleClick));
101
- connect(lineSpace, "dragstart", onDragStart);
102
101
  connect(lineSpace, "selectstart", e_preventDefault);
103
102
  // Gecko browsers fire contextmenu *after* opening the menu, at
104
103
  // which point we can't mess with it anymore. Context menu is
@@ -118,9 +117,16 @@ var CodeMirror = (function() {
118
117
  connect(input, "focus", onFocus);
119
118
  connect(input, "blur", onBlur);
120
119
 
121
- connect(scroller, "dragenter", e_stop);
122
- connect(scroller, "dragover", e_stop);
123
- connect(scroller, "drop", operation(onDrop));
120
+ if (options.dragDrop) {
121
+ connect(lineSpace, "dragstart", onDragStart);
122
+ function drag_(e) {
123
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
124
+ e_stop(e);
125
+ }
126
+ connect(scroller, "dragenter", drag_);
127
+ connect(scroller, "dragover", drag_);
128
+ connect(scroller, "drop", operation(onDrop));
129
+ }
124
130
  connect(scroller, "paste", function(){focusInput(); fastPoll();});
125
131
  connect(input, "paste", fastPoll);
126
132
  connect(input, "cut", operation(function(){
@@ -312,6 +318,7 @@ var CodeMirror = (function() {
312
318
  },
313
319
 
314
320
  operation: function(f){return operation(f)();},
321
+ compoundChange: function(f){return compoundChange(f);},
315
322
  refresh: function(){
316
323
  updateDisplay(true);
317
324
  if (scroller.scrollHeight > lastScrollPos)
@@ -336,7 +343,7 @@ var CodeMirror = (function() {
336
343
  splitLines(code), top, top);
337
344
  updateInput = true;
338
345
  }
339
- function getValue(code) {
346
+ function getValue() {
340
347
  var text = [];
341
348
  doc.iter(0, doc.size, function(line) { text.push(line.text); });
342
349
  return text.join("\n");
@@ -385,20 +392,22 @@ var CodeMirror = (function() {
385
392
  } else { lastClick = {time: now, pos: start}; }
386
393
 
387
394
  var last = start, going;
388
- if (dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
395
+ if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
389
396
  !posLess(start, sel.from) && !posLess(sel.to, start)) {
390
397
  // Let the drag handler handle this.
391
398
  if (webkit) lineSpace.draggable = true;
392
- var up = connect(document, "mouseup", operation(function(e2) {
399
+ function dragEnd(e2) {
393
400
  if (webkit) lineSpace.draggable = false;
394
401
  draggingText = false;
395
- up();
402
+ up(); drop();
396
403
  if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
397
404
  e_preventDefault(e2);
398
405
  setCursor(start.line, start.ch, true);
399
406
  focusInput();
400
407
  }
401
- }), true);
408
+ }
409
+ var up = connect(document, "mouseup", operation(dragEnd), true);
410
+ var drop = connect(scroller, "drop", operation(dragEnd), true);
402
411
  draggingText = true;
403
412
  // IE's approach to draggable
404
413
  if (lineSpace.dragDrop) lineSpace.dragDrop();
@@ -447,6 +456,7 @@ var CodeMirror = (function() {
447
456
  selectWordAt(start);
448
457
  }
449
458
  function onDrop(e) {
459
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
450
460
  e.preventDefault();
451
461
  var pos = posFromMouse(e, true), files = e.dataTransfer.files;
452
462
  if (!pos || options.readOnly) return;
@@ -456,12 +466,12 @@ var CodeMirror = (function() {
456
466
  reader.onload = function() {
457
467
  text[i] = reader.result;
458
468
  if (++read == n) {
459
- pos = clipPos(pos);
460
- operation(function() {
469
+ pos = clipPos(pos);
470
+ operation(function() {
461
471
  var end = replaceRange(text.join(""), pos, pos);
462
472
  setSelectionUser(pos, end);
463
473
  })();
464
- }
474
+ }
465
475
  };
466
476
  reader.readAsText(file);
467
477
  }
@@ -472,12 +482,14 @@ var CodeMirror = (function() {
472
482
  try {
473
483
  var text = e.dataTransfer.getData("Text");
474
484
  if (text) {
475
- var curFrom = sel.from, curTo = sel.to;
476
- setSelectionUser(pos, pos);
477
- if (draggingText) replaceRange("", curFrom, curTo);
478
- replaceSelection(text);
479
- focusInput();
480
- }
485
+ compoundChange(function() {
486
+ var curFrom = sel.from, curTo = sel.to;
487
+ setSelectionUser(pos, pos);
488
+ if (draggingText) replaceRange("", curFrom, curTo);
489
+ replaceSelection(text);
490
+ focusInput();
491
+ });
492
+ }
481
493
  }
482
494
  catch(e){}
483
495
  }
@@ -529,15 +541,19 @@ var CodeMirror = (function() {
529
541
  if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
530
542
  if (e_prop(e, "metaKey")) name = "Cmd-" + name;
531
543
 
544
+ var stopped = false;
545
+ function stop() { stopped = true; }
546
+
532
547
  if (e_prop(e, "shiftKey")) {
533
548
  handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
534
- function(b) {return doHandleBinding(b, true);})
549
+ function(b) {return doHandleBinding(b, true);}, stop)
535
550
  || lookupKey(name, options.extraKeys, options.keyMap, function(b) {
536
551
  if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
537
- });
552
+ }, stop);
538
553
  } else {
539
- handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding);
554
+ handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop);
540
555
  }
556
+ if (stopped) handled = false;
541
557
  if (handled) {
542
558
  e_preventDefault(e);
543
559
  if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
@@ -546,7 +562,7 @@ var CodeMirror = (function() {
546
562
  }
547
563
  function handleCharBinding(e, ch) {
548
564
  var handled = lookupKey("'" + ch + "'", options.extraKeys,
549
- options.keyMap, doHandleBinding);
565
+ options.keyMap, function(b) { return doHandleBinding(b, true); });
550
566
  if (handled) e_preventDefault(e);
551
567
  return handled;
552
568
  }
@@ -648,7 +664,7 @@ var CodeMirror = (function() {
648
664
  if (suppressEdits) return;
649
665
  var recomputeMaxLength = false, maxLineLength = maxLine.length;
650
666
  if (!options.lineWrapping)
651
- doc.iter(from.line, to.line, function(line) {
667
+ doc.iter(from.line, to.line + 1, function(line) {
652
668
  if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
653
669
  });
654
670
  if (from.line != to.line || newText.length > 1) gutterDirty = true;
@@ -696,14 +712,14 @@ var CodeMirror = (function() {
696
712
  doc.insert(from.line + 1, added);
697
713
  }
698
714
  if (options.lineWrapping) {
699
- var perLine = scroller.clientWidth / charWidth() - 3;
715
+ var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
700
716
  doc.iter(from.line, from.line + newText.length, function(line) {
701
717
  if (line.hidden) return;
702
718
  var guess = Math.ceil(line.text.length / perLine) || 1;
703
719
  if (guess != line.height) updateLineHeight(line, guess);
704
720
  });
705
721
  } else {
706
- doc.iter(from.line, i + newText.length, function(line) {
722
+ doc.iter(from.line, from.line + newText.length, function(line) {
707
723
  var l = line.text;
708
724
  if (l.length > maxLineLength) {
709
725
  maxLine = l; maxLineLength = l.length; maxWidth = null;
@@ -874,8 +890,9 @@ var CodeMirror = (function() {
874
890
 
875
891
  var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
876
892
  var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
877
- if (x1 < screenleft + gutterw) {
878
- if (x1 < 50) x1 = 0;
893
+ var atLeft = x1 < gutterw + pl + 10;
894
+ if (x1 < screenleft + gutterw || atLeft) {
895
+ if (atLeft) x1 = 0;
879
896
  scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
880
897
  scrolled = true;
881
898
  }
@@ -890,10 +907,10 @@ var CodeMirror = (function() {
890
907
 
891
908
  function visibleLines() {
892
909
  var lh = textHeight(), top = scroller.scrollTop - paddingTop();
893
- var from_height = Math.max(0, Math.floor(top / lh));
894
- var to_height = Math.ceil((top + scroller.clientHeight) / lh);
895
- return {from: lineAtHeight(doc, from_height),
896
- to: lineAtHeight(doc, to_height)};
910
+ var fromHeight = Math.max(0, Math.floor(top / lh));
911
+ var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
912
+ return {from: lineAtHeight(doc, fromHeight),
913
+ to: lineAtHeight(doc, toHeight)};
897
914
  }
898
915
  // Uses a set of changes plus the current scroll position to
899
916
  // determine which DOM updates have to be made, and makes the
@@ -924,7 +941,7 @@ var CodeMirror = (function() {
924
941
  if (range.from >= range.to) intact.splice(i--, 1);
925
942
  else intactLines += range.to - range.from;
926
943
  }
927
- if (intactLines == to - from) return;
944
+ if (intactLines == to - from && from == showingFrom && to == showingTo) return;
928
945
  intact.sort(function(a, b) {return a.domStart - b.domStart;});
929
946
 
930
947
  var th = textHeight(), gutterDisplay = gutter.style.display;
@@ -1108,12 +1125,14 @@ var CodeMirror = (function() {
1108
1125
  selectionDiv.style.display = "none";
1109
1126
  } else {
1110
1127
  var sameLine = fromPos.y == toPos.y, html = "";
1128
+ var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1129
+ var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1111
1130
  function add(left, top, right, height) {
1131
+ var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
1132
+ : "right: " + right + "px";
1112
1133
  html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
1113
- 'px; top: ' + top + 'px; right: ' + right + 'px; height: ' + height + 'px"></div>';
1134
+ 'px; top: ' + top + 'px; ' + rstyle + '; height: ' + height + 'px"></div>';
1114
1135
  }
1115
- var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1116
- var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1117
1136
  if (sel.from.ch && fromPos.y >= 0) {
1118
1137
  var right = sameLine ? clientWidth - toPos.x : 0;
1119
1138
  add(fromPos.x, fromPos.y, right, th);
@@ -1189,13 +1208,14 @@ var CodeMirror = (function() {
1189
1208
  var line = getLine(lNo);
1190
1209
  if (!line.hidden) {
1191
1210
  var ch = pos.ch;
1192
- if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1211
+ if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
1193
1212
  return {line: lNo, ch: ch};
1194
1213
  }
1195
1214
  lNo += dir;
1196
1215
  }
1197
1216
  }
1198
1217
  var line = getLine(pos.line);
1218
+ var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
1199
1219
  if (!line.hidden) return pos;
1200
1220
  if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1201
1221
  else return getNonHidden(-1) || getNonHidden(1);
@@ -1355,7 +1375,7 @@ var CodeMirror = (function() {
1355
1375
  return (tabCache[w] = {html: str + "</span>", width: w});
1356
1376
  }
1357
1377
  function themeChanged() {
1358
- scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
1378
+ scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1359
1379
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1360
1380
  }
1361
1381
 
@@ -1506,8 +1526,7 @@ var CodeMirror = (function() {
1506
1526
  if (x <= 0) return 0;
1507
1527
  var lineObj = getLine(line), text = lineObj.text;
1508
1528
  function getX(len) {
1509
- measure.innerHTML = "<pre><span>" + lineObj.getHTML(makeTab, len) + "</span></pre>";
1510
- return measure.firstChild.firstChild.offsetWidth;
1529
+ return measureLine(lineObj, len).left;
1511
1530
  }
1512
1531
  var from = 0, fromX = 0, to = text.length, toX;
1513
1532
  // Guess a suitable upper bound for our search.
@@ -1530,19 +1549,13 @@ var CodeMirror = (function() {
1530
1549
  }
1531
1550
  }
1532
1551
 
1533
- var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1552
+ var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16);
1534
1553
  function measureLine(line, ch) {
1535
1554
  if (ch == 0) return {top: 0, left: 0};
1536
- var extra = "";
1537
- // Include extra text at the end to make sure the measured line is wrapped in the right way.
1538
- if (options.lineWrapping) {
1539
- var end = line.text.indexOf(" ", ch + 6);
1540
- extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1541
- }
1542
- measure.innerHTML = "<pre>" + line.getHTML(makeTab, ch) +
1543
- '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1544
- extra + "</pre>";
1545
- var elt = document.getElementById("CodeMirror-temp-" + tempId);
1555
+ var wbr = options.lineWrapping && ch < line.text.length &&
1556
+ spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
1557
+ measure.innerHTML = "<pre>" + line.getHTML(makeTab, ch, tempId, wbr) + "</pre>";
1558
+ var elt = document.getElementById(tempId);
1546
1559
  var top = elt.offsetTop, left = elt.offsetLeft;
1547
1560
  // Older IEs report zero offsets for spans directly after a wrap
1548
1561
  if (ie && top == 0 && left == 0) {
@@ -1790,13 +1803,17 @@ var CodeMirror = (function() {
1790
1803
  var changed = line.highlight(mode, state, options.tabSize);
1791
1804
  if (changed) realChange = true;
1792
1805
  line.stateAfter = copyState(mode, state);
1806
+ var done = null;
1793
1807
  if (compare) {
1794
- if (hadState && compare(hadState, state)) return true;
1795
- } else {
1808
+ var same = hadState && compare(hadState, state);
1809
+ if (same != Pass) done = !!same;
1810
+ }
1811
+ if (done == null) {
1796
1812
  if (changed !== false || !hadState) unchanged = 0;
1797
1813
  else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1798
- return true;
1814
+ done = true;
1799
1815
  }
1816
+ if (done) return true;
1800
1817
  ++i;
1801
1818
  });
1802
1819
  if (bail) return;
@@ -1856,6 +1873,11 @@ var CodeMirror = (function() {
1856
1873
  };
1857
1874
  }
1858
1875
 
1876
+ function compoundChange(f) {
1877
+ history.startCompound();
1878
+ try { return f(); } finally { history.endCompound(); }
1879
+ }
1880
+
1859
1881
  for (var ext in extensions)
1860
1882
  if (extensions.propertyIsEnumerable(ext) &&
1861
1883
  !instance.propertyIsEnumerable(ext))
@@ -1877,12 +1899,14 @@ var CodeMirror = (function() {
1877
1899
  electricChars: true,
1878
1900
  autoClearEmptyLines: false,
1879
1901
  onKeyEvent: null,
1902
+ onDragEvent: null,
1880
1903
  lineWrapping: false,
1881
1904
  lineNumbers: false,
1882
1905
  gutter: false,
1883
1906
  fixedGutter: false,
1884
1907
  firstLineNumber: 1,
1885
1908
  readOnly: false,
1909
+ dragDrop: true,
1886
1910
  onChange: null,
1887
1911
  onCursorActivity: null,
1888
1912
  onGutterClick: null,
@@ -1906,6 +1930,10 @@ var CodeMirror = (function() {
1906
1930
  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
1907
1931
  CodeMirror.defineMode = function(name, mode) {
1908
1932
  if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1933
+ if (arguments.length > 2) {
1934
+ mode.dependencies = [];
1935
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
1936
+ }
1909
1937
  modes[name] = mode;
1910
1938
  };
1911
1939
  CodeMirror.defineMIME = function(mime, spec) {
@@ -1922,10 +1950,7 @@ var CodeMirror = (function() {
1922
1950
  CodeMirror.getMode = function(options, spec) {
1923
1951
  var spec = CodeMirror.resolveMode(spec);
1924
1952
  var mfactory = modes[spec.name];
1925
- if (!mfactory) {
1926
- if (window.console) console.warn("No mode " + spec.name + " found, falling back to plain text.");
1927
- return CodeMirror.getMode(options, "text/plain");
1928
- }
1953
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
1929
1954
  return mfactory(options, spec);
1930
1955
  };
1931
1956
  CodeMirror.listModes = function() {
@@ -2035,12 +2060,15 @@ var CodeMirror = (function() {
2035
2060
  if (typeof val == "string") return keyMap[val];
2036
2061
  else return val;
2037
2062
  }
2038
- function lookupKey(name, extraMap, map, handle) {
2063
+ function lookupKey(name, extraMap, map, handle, stop) {
2039
2064
  function lookup(map) {
2040
2065
  map = getKeyMap(map);
2041
2066
  var found = map[name];
2042
2067
  if (found != null && handle(found)) return true;
2043
- if (map.catchall) return handle(map.catchall);
2068
+ if (map.nofallthrough) {
2069
+ if (stop) stop();
2070
+ return true;
2071
+ }
2044
2072
  var fallthrough = map.fallthrough;
2045
2073
  if (fallthrough == null) return false;
2046
2074
  if (Object.prototype.toString.call(fallthrough) != "[object Array]")
@@ -2402,9 +2430,9 @@ var CodeMirror = (function() {
2402
2430
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2403
2431
  // Produces an HTML fragment for the line, taking selection,
2404
2432
  // marking, and highlighting into account.
2405
- getHTML: function(makeTab, endAt) {
2433
+ getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) {
2406
2434
  var html = [], first = true, col = 0;
2407
- function span(text, style) {
2435
+ function span_(text, style) {
2408
2436
  if (!text) return;
2409
2437
  // Work around a bug where, in some compat modes, IE ignores leading spaces
2410
2438
  if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
@@ -2432,24 +2460,50 @@ var CodeMirror = (function() {
2432
2460
  if (style) html.push('<span class="', style, '">', escaped, "</span>");
2433
2461
  else html.push(escaped);
2434
2462
  }
2463
+ var span = span_;
2464
+ if (wrapAt != null) {
2465
+ var outPos = 0, open = "<span id=\"" + wrapId + "\">";
2466
+ span = function(text, style) {
2467
+ var l = text.length;
2468
+ if (wrapAt >= outPos && wrapAt < outPos + l) {
2469
+ if (wrapAt > outPos) {
2470
+ span_(text.slice(0, wrapAt - outPos), style);
2471
+ // See comment at the definition of spanAffectsWrapping
2472
+ if (wrapWBR) html.push("<wbr>");
2473
+ }
2474
+ html.push(open);
2475
+ span_(text.slice(wrapAt - outPos), style);
2476
+ html.push("</span>");
2477
+ wrapAt--;
2478
+ outPos += l;
2479
+ } else {
2480
+ outPos += l;
2481
+ span_(text, style);
2482
+ // Output empty wrapper when at end of line
2483
+ if (outPos == wrapAt && outPos == len) html.push(open + "</span>");
2484
+ // Stop outputting HTML when gone sufficiently far beyond measure
2485
+ else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2486
+ }
2487
+ }
2488
+ }
2489
+
2435
2490
  var st = this.styles, allText = this.text, marked = this.marked;
2436
2491
  var len = allText.length;
2437
- if (endAt != null) len = Math.min(endAt, len);
2438
2492
  function styleToClass(style) {
2439
2493
  if (!style) return null;
2440
2494
  return "cm-" + style.replace(/ +/g, " cm-");
2441
2495
  }
2442
2496
 
2443
- if (!allText && endAt == null)
2497
+ if (!allText && wrapAt == null) {
2444
2498
  span(" ");
2445
- else if (!marked || !marked.length)
2499
+ } else if (!marked || !marked.length) {
2446
2500
  for (var i = 0, ch = 0; ch < len; i+=2) {
2447
2501
  var str = st[i], style = st[i+1], l = str.length;
2448
2502
  if (ch + l > len) str = str.slice(0, len - ch);
2449
2503
  ch += l;
2450
2504
  span(str, styleToClass(style));
2451
2505
  }
2452
- else {
2506
+ } else {
2453
2507
  var pos = 0, i = 0, text = "", style, sg = 0;
2454
2508
  var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2455
2509
  function advanceMarks() {
@@ -2535,7 +2589,11 @@ var CodeMirror = (function() {
2535
2589
  },
2536
2590
  insertHeight: function(at, lines, height) {
2537
2591
  this.height += height;
2538
- this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2592
+ // The trick below is apparently too advanced for IE, which
2593
+ // occasionally corrupts this.lines (duplicating elements) when
2594
+ // it is used.
2595
+ if (ie) this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
2596
+ else this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2539
2597
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2540
2598
  },
2541
2599
  iterN: function(at, n, op) {
@@ -2701,33 +2759,36 @@ var CodeMirror = (function() {
2701
2759
  function History() {
2702
2760
  this.time = 0;
2703
2761
  this.done = []; this.undone = [];
2762
+ this.compound = 0;
2763
+ this.closed = false;
2704
2764
  }
2705
2765
  History.prototype = {
2706
2766
  addChange: function(start, added, old) {
2707
2767
  this.undone.length = 0;
2708
2768
  var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2709
2769
  var dtime = time - this.time;
2710
- if (dtime > 400 || !last) {
2711
- this.done.push([{start: start, added: added, old: old}]);
2712
- } else if (last.start > start + old.length || last.start + last.added < start - last.added + last.old.length) {
2770
+
2771
+ if (this.compound && cur && !this.closed) {
2713
2772
  cur.push({start: start, added: added, old: old});
2773
+ } else if (dtime > 400 || !last || this.closed ||
2774
+ last.start > start + old.length || last.start + last.added < start) {
2775
+ this.done.push([{start: start, added: added, old: old}]);
2776
+ this.closed = false;
2714
2777
  } else {
2715
- var oldoff = 0;
2716
- if (start < last.start) {
2717
- for (var i = last.start - start - 1; i >= 0; --i)
2718
- last.old.unshift(old[i]);
2719
- oldoff = Math.min(0, added - old.length);
2720
- last.added += last.start - start + oldoff;
2721
- last.start = start;
2722
- } else if (last.start < start) {
2723
- oldoff = start - last.start;
2724
- added += oldoff;
2725
- }
2726
- for (var i = last.added - oldoff, e = old.length; i < e; ++i)
2727
- last.old.push(old[i]);
2728
- if (last.added < added) last.added = added;
2778
+ var startBefore = Math.max(0, last.start - start),
2779
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
2780
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
2781
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
2782
+ if (startBefore) last.start = start;
2783
+ last.added += added - (old.length - startBefore - endAfter);
2729
2784
  }
2730
2785
  this.time = time;
2786
+ },
2787
+ startCompound: function() {
2788
+ if (!this.compound++) this.closed = true;
2789
+ },
2790
+ endCompound: function() {
2791
+ if (!--this.compound) this.closed = true;
2731
2792
  }
2732
2793
  };
2733
2794
 
@@ -2789,8 +2850,10 @@ var CodeMirror = (function() {
2789
2850
  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2790
2851
  var ie = /MSIE \d/.test(navigator.userAgent);
2791
2852
  var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2853
+ var quirksMode = ie && document.documentMode == 5;
2792
2854
  var webkit = /WebKit\//.test(navigator.userAgent);
2793
2855
  var chrome = /Chrome\//.test(navigator.userAgent);
2856
+ var safari = /Apple Computer/.test(navigator.vendor);
2794
2857
  var khtml = /KHTML\//.test(navigator.userAgent);
2795
2858
 
2796
2859
  // Detect drag-and-drop
@@ -2802,13 +2865,25 @@ var CodeMirror = (function() {
2802
2865
  return "draggable" in div || "dragDrop" in div;
2803
2866
  }();
2804
2867
 
2805
- var lineSep = "\n";
2806
2868
  // Feature-detect whether newlines in textareas are converted to \r\n
2807
- (function () {
2869
+ var lineSep = function () {
2808
2870
  var te = document.createElement("textarea");
2809
2871
  te.value = "foo\nbar";
2810
- if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2811
- }());
2872
+ if (te.value.indexOf("\r") > -1) return "\r\n";
2873
+ return "\n";
2874
+ }();
2875
+
2876
+ // For a reason I have yet to figure out, some browsers disallow
2877
+ // word wrapping between certain characters *only* if a new inline
2878
+ // element is started between them. This makes it hard to reliably
2879
+ // measure the position of things, since that requires inserting an
2880
+ // extra span. This terribly fragile set of regexps matches the
2881
+ // character combinations that suffer from this phenomenon on the
2882
+ // various browsers.
2883
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
2884
+ if (gecko) spanAffectsWrapping = /$'/;
2885
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
2886
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
2812
2887
 
2813
2888
  // Counts the column offset in a string, taking tabs into account.
2814
2889
  // Used mostly to find indentation.