codemirror-rails 4.7 → 4.8

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 (26) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +229 -137
  4. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +1 -1
  5. data/vendor/assets/javascripts/codemirror/addons/hint/javascript-hint.js +9 -9
  6. data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +9 -1
  7. data/vendor/assets/javascripts/codemirror/addons/mode/loadmode.js +19 -16
  8. data/vendor/assets/javascripts/codemirror/addons/mode/overlay.js +3 -3
  9. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +19 -28
  10. data/vendor/assets/javascripts/codemirror/keymaps/sublime.js +14 -15
  11. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +694 -752
  12. data/vendor/assets/javascripts/codemirror/modes/clike.js +15 -0
  13. data/vendor/assets/javascripts/codemirror/modes/css.js +1 -1
  14. data/vendor/assets/javascripts/codemirror/modes/dockerfile.js +80 -0
  15. data/vendor/assets/javascripts/codemirror/modes/gfm.js +2 -1
  16. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +2 -2
  17. data/vendor/assets/javascripts/codemirror/modes/idl.js +290 -0
  18. data/vendor/assets/javascripts/codemirror/modes/markdown.js +62 -55
  19. data/vendor/assets/javascripts/codemirror/modes/sparql.js +19 -5
  20. data/vendor/assets/javascripts/codemirror/modes/sql.js +0 -2
  21. data/vendor/assets/javascripts/codemirror/modes/stex.js +184 -193
  22. data/vendor/assets/javascripts/codemirror/modes/yaml.js +6 -1
  23. data/vendor/assets/stylesheets/codemirror.css +11 -2
  24. data/vendor/assets/stylesheets/codemirror/themes/3024-day.css +1 -1
  25. data/vendor/assets/stylesheets/codemirror/themes/solarized.css +0 -5
  26. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 926ba7eaf6d3a5946a51a9edd1dc6d99ed211cb8
4
- data.tar.gz: e2c1f08a582fd76a0ecc35be5a393b1d2c0f25ff
3
+ metadata.gz: 0d7ed536cdd6c54bf26a2267fc6cf13ed09e4624
4
+ data.tar.gz: 62813e43c76250a3ff56dc69e5d4123a1d42217f
5
5
  SHA512:
6
- metadata.gz: ac13c0485f7879662c60063b1e4cc13b8e7b8e4c733806c14a5099f6a506c6c7b735b99ace5f6596c3f5225a3816db098d1878d48ce2ec640c5d985d8458a649
7
- data.tar.gz: ca0484ab6b9734f44a2a160d471a6584867b3d22f374485ea5c1810b898fd6859a9e86be18f762e3e1ec99d383d291dd8dbdc2c6a5f870157c90978de28d1325
6
+ metadata.gz: 3317a5d5a1f262e96ee51ce496be3108871e07f081140d3c35636beba654d2dc665a10004384f6cb49a7d6feca12a5aea7c634675cb905819226d32ed8798b79
7
+ data.tar.gz: f48c7e94303a13be7f6ef197dc5346ed5c113c829fcf9b8be8e858511b989c641bf7957082514f86df44e32140d8ee5c296118ff2e29f6703b6ce886f8ca608c
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '4.7'
4
- CODEMIRROR_VERSION = '4.7'
3
+ VERSION = '4.8'
4
+ CODEMIRROR_VERSION = '4.8'
5
5
  end
6
6
  end
@@ -86,7 +86,8 @@
86
86
  suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
87
87
  pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
88
88
  draggingText: false,
89
- highlight: new Delayed() // stores highlight worker timeout
89
+ highlight: new Delayed(), // stores highlight worker timeout
90
+ keySeq: null // Unfinished key sequence
90
91
  };
91
92
 
92
93
  // Override magic textarea content restore that IE sometimes does
@@ -184,8 +185,10 @@
184
185
  // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
185
186
  if (ie && ie_version < 8) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = "18px";
186
187
 
187
- if (place.appendChild) place.appendChild(d.wrapper);
188
- else place(d.wrapper);
188
+ if (place) {
189
+ if (place.appendChild) place.appendChild(d.wrapper);
190
+ else place(d.wrapper);
191
+ }
189
192
 
190
193
  // Current rendered range (may be bigger than the view window).
191
194
  d.viewFrom = d.viewTo = doc.first;
@@ -196,7 +199,7 @@
196
199
  d.externalMeasured = null;
197
200
  // Empty space (in pixels) above the view
198
201
  d.viewOffset = 0;
199
- d.lastSizeC = 0;
202
+ d.lastWrapHeight = d.lastWrapWidth = 0;
200
203
  d.updateLineNumbers = null;
201
204
 
202
205
  // Used to only resize the line number gutter when necessary (when
@@ -301,12 +304,6 @@
301
304
  });
302
305
  }
303
306
 
304
- function keyMapChanged(cm) {
305
- var map = keyMap[cm.options.keyMap], style = map.style;
306
- cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
307
- (style ? " cm-keymap-" + style : "");
308
- }
309
-
310
307
  function themeChanged(cm) {
311
308
  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
312
309
  cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
@@ -551,6 +548,7 @@
551
548
  this.visible = visibleLines(display, cm.doc, viewport);
552
549
  this.editorIsHidden = !display.wrapper.offsetWidth;
553
550
  this.wrapperHeight = display.wrapper.clientHeight;
551
+ this.wrapperWidth = display.wrapper.clientWidth;
554
552
  this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
555
553
  this.oldScrollerWidth = display.scroller.clientWidth;
556
554
  this.force = force;
@@ -591,7 +589,7 @@
591
589
  }
592
590
 
593
591
  var different = from != display.viewFrom || to != display.viewTo ||
594
- display.lastSizeC != update.wrapperHeight;
592
+ display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
595
593
  adjustView(cm, from, to);
596
594
 
597
595
  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
@@ -619,7 +617,8 @@
619
617
  removeChildren(display.selectionDiv);
620
618
 
621
619
  if (different) {
622
- display.lastSizeC = update.wrapperHeight;
620
+ display.lastWrapHeight = update.wrapperHeight;
621
+ display.lastWrapWidth = update.wrapperWidth;
623
622
  startWorker(cm, 400);
624
623
  }
625
624
 
@@ -868,9 +867,12 @@
868
867
  if (cm.options.lineNumbers || markers) {
869
868
  var wrap = ensureLineWrapped(lineView);
870
869
  var gutterWrap = lineView.gutter =
871
- wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " +
872
- (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
870
+ wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
871
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
872
+ "px; width: " + dims.gutterTotalWidth + "px"),
873
873
  lineView.text);
874
+ if (lineView.line.gutterClass)
875
+ gutterWrap.className += " " + lineView.line.gutterClass;
874
876
  if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
875
877
  lineView.lineNumber = gutterWrap.appendChild(
876
878
  elt("div", lineNumberFor(cm.options, lineN),
@@ -2397,7 +2399,7 @@
2397
2399
  // possible when it is clear that nothing happened. hasSelection
2398
2400
  // will be the case when there is a lot of text in the textarea,
2399
2401
  // in which case reading its value would be expensive.
2400
- if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput)
2402
+ if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
2401
2403
  return false;
2402
2404
  // See paste handler for more on the fakedLastChar kludge
2403
2405
  if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
@@ -2652,8 +2654,10 @@
2652
2654
 
2653
2655
  // Called when the window resizes
2654
2656
  function onResize(cm) {
2655
- // Might be a text scaling operation, clear size caches.
2656
2657
  var d = cm.display;
2658
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
2659
+ return;
2660
+ // Might be a text scaling operation, clear size caches.
2657
2661
  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
2658
2662
  cm.setSize();
2659
2663
  }
@@ -3167,62 +3171,70 @@
3167
3171
  return done;
3168
3172
  }
3169
3173
 
3170
- // Collect the currently active keymaps.
3171
- function allKeyMaps(cm) {
3172
- var maps = cm.state.keyMaps.slice(0);
3173
- if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
3174
- maps.push(cm.options.keyMap);
3175
- return maps;
3174
+ function lookupKeyForEditor(cm, name, handle) {
3175
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
3176
+ var result = lookupKey(name, cm.state.keyMaps[i], handle);
3177
+ if (result) return result;
3178
+ }
3179
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle))
3180
+ || lookupKey(name, cm.options.keyMap, handle);
3181
+ }
3182
+
3183
+ var stopSeq = new Delayed;
3184
+ function dispatchKey(cm, name, e, handle) {
3185
+ var seq = cm.state.keySeq;
3186
+ if (seq) {
3187
+ if (isModifierKey(name)) return "handled";
3188
+ stopSeq.set(50, function() {
3189
+ if (cm.state.keySeq == seq) {
3190
+ cm.state.keySeq = null;
3191
+ resetInput(cm);
3192
+ }
3193
+ });
3194
+ name = seq + " " + name;
3195
+ }
3196
+ var result = lookupKeyForEditor(cm, name, handle);
3197
+
3198
+ if (result == "multi")
3199
+ cm.state.keySeq = name;
3200
+ if (result == "handled")
3201
+ signalLater(cm, "keyHandled", cm, name, e);
3202
+
3203
+ if (result == "handled" || result == "multi") {
3204
+ e_preventDefault(e);
3205
+ restartBlink(cm);
3206
+ }
3207
+
3208
+ if (seq && !result && /\'$/.test(name)) {
3209
+ e_preventDefault(e);
3210
+ return true;
3211
+ }
3212
+ return !!result;
3176
3213
  }
3177
3214
 
3178
- var maybeTransition;
3179
3215
  // Handle a key from the keydown event.
3180
3216
  function handleKeyBinding(cm, e) {
3181
- // Handle automatic keymap transitions
3182
- var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
3183
- clearTimeout(maybeTransition);
3184
- if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
3185
- if (getKeyMap(cm.options.keyMap) == startMap) {
3186
- cm.options.keyMap = (next.call ? next.call(null, cm) : next);
3187
- keyMapChanged(cm);
3188
- }
3189
- }, 50);
3190
-
3191
- var name = keyName(e, true), handled = false;
3217
+ var name = keyName(e, true);
3192
3218
  if (!name) return false;
3193
- var keymaps = allKeyMaps(cm);
3194
3219
 
3195
- if (e.shiftKey) {
3220
+ if (e.shiftKey && !cm.state.keySeq) {
3196
3221
  // First try to resolve full name (including 'Shift-'). Failing
3197
3222
  // that, see if there is a cursor-motion command (starting with
3198
3223
  // 'go') bound to the keyname without 'Shift-'.
3199
- handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
3200
- || lookupKey(name, keymaps, function(b) {
3201
- if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
3202
- return doHandleBinding(cm, b);
3203
- });
3224
+ return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
3225
+ || dispatchKey(cm, name, e, function(b) {
3226
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
3227
+ return doHandleBinding(cm, b);
3228
+ });
3204
3229
  } else {
3205
- handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
3230
+ return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
3206
3231
  }
3207
-
3208
- if (handled) {
3209
- e_preventDefault(e);
3210
- restartBlink(cm);
3211
- signalLater(cm, "keyHandled", cm, name, e);
3212
- }
3213
- return handled;
3214
3232
  }
3215
3233
 
3216
3234
  // Handle a key from the keypress event
3217
3235
  function handleCharBinding(cm, e, ch) {
3218
- var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
3219
- function(b) { return doHandleBinding(cm, b, true); });
3220
- if (handled) {
3221
- e_preventDefault(e);
3222
- restartBlink(cm);
3223
- signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
3224
- }
3225
- return handled;
3236
+ return dispatchKey(cm, "'" + ch + "'", e,
3237
+ function(b) { return doHandleBinding(cm, b, true); });
3226
3238
  }
3227
3239
 
3228
3240
  var lastStoppedKey = null;
@@ -3701,6 +3713,8 @@
3701
3713
  // If an editor sits on the top or bottom of the window, partially
3702
3714
  // scrolled out of view, this ensures that the cursor is visible.
3703
3715
  function maybeScrollWindow(cm, coords) {
3716
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
3717
+
3704
3718
  var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
3705
3719
  if (coords.top + box.top < 0) doScroll = true;
3706
3720
  else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
@@ -4024,12 +4038,12 @@
4024
4038
  getDoc: function() {return this.doc;},
4025
4039
 
4026
4040
  addKeyMap: function(map, bottom) {
4027
- this.state.keyMaps[bottom ? "push" : "unshift"](map);
4041
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4028
4042
  },
4029
4043
  removeKeyMap: function(map) {
4030
4044
  var maps = this.state.keyMaps;
4031
4045
  for (var i = 0; i < maps.length; ++i)
4032
- if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
4046
+ if (maps[i] == map || maps[i].name == map) {
4033
4047
  maps.splice(i, 1);
4034
4048
  return true;
4035
4049
  }
@@ -4086,20 +4100,11 @@
4086
4100
  // Fetch the parser token for a given character. Useful for hacks
4087
4101
  // that want to inspect the mode state (say, for completion).
4088
4102
  getTokenAt: function(pos, precise) {
4089
- var doc = this.doc;
4090
- pos = clipPos(doc, pos);
4091
- var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
4092
- var line = getLine(doc, pos.line);
4093
- var stream = new StringStream(line.text, this.options.tabSize);
4094
- while (stream.pos < pos.ch && !stream.eol()) {
4095
- stream.start = stream.pos;
4096
- var style = readToken(mode, stream, state);
4097
- }
4098
- return {start: stream.start,
4099
- end: stream.pos,
4100
- string: stream.current(),
4101
- type: style || null,
4102
- state: state};
4103
+ return takeToken(this, pos, precise);
4104
+ },
4105
+
4106
+ getLineTokens: function(line, precise) {
4107
+ return takeToken(this, Pos(line), precise, true);
4103
4108
  },
4104
4109
 
4105
4110
  getTokenTypeAt: function(pos) {
@@ -4502,7 +4507,12 @@
4502
4507
  themeChanged(cm);
4503
4508
  guttersChanged(cm);
4504
4509
  }, true);
4505
- option("keyMap", "default", keyMapChanged);
4510
+ option("keyMap", "default", function(cm, val, old) {
4511
+ var next = getKeyMap(val);
4512
+ var prev = old != CodeMirror.Init && getKeyMap(old);
4513
+ if (prev && prev.detach) prev.detach(cm, next);
4514
+ if (next.attach) next.attach(cm, prev || null);
4515
+ });
4506
4516
  option("extraKeys", null);
4507
4517
 
4508
4518
  option("lineWrapping", false, wrappingChanged, true);
@@ -4847,9 +4857,11 @@
4847
4857
  toggleOverwrite: function(cm) {cm.toggleOverwrite();}
4848
4858
  };
4849
4859
 
4860
+
4850
4861
  // STANDARD KEYMAPS
4851
4862
 
4852
4863
  var keyMap = CodeMirror.keyMap = {};
4864
+
4853
4865
  keyMap.basic = {
4854
4866
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
4855
4867
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
@@ -4871,6 +4883,13 @@
4871
4883
  "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
4872
4884
  fallthrough: "basic"
4873
4885
  };
4886
+ // Very basic readline/emacs-style bindings, which are standard on Mac.
4887
+ keyMap.emacsy = {
4888
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
4889
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
4890
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
4891
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
4892
+ };
4874
4893
  keyMap.macDefault = {
4875
4894
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
4876
4895
  "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
@@ -4881,70 +4900,100 @@
4881
4900
  "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
4882
4901
  fallthrough: ["basic", "emacsy"]
4883
4902
  };
4884
- // Very basic readline/emacs-style bindings, which are standard on Mac.
4885
- keyMap.emacsy = {
4886
- "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
4887
- "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
4888
- "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
4889
- "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
4890
- };
4891
4903
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
4892
4904
 
4893
4905
  // KEYMAP DISPATCH
4894
4906
 
4895
- function getKeyMap(val) {
4896
- if (typeof val == "string") return keyMap[val];
4897
- else return val;
4898
- }
4899
-
4900
- // Given an array of keymaps and a key name, call handle on any
4901
- // bindings found, until that returns a truthy value, at which point
4902
- // we consider the key handled. Implements things like binding a key
4903
- // to false stopping further handling and keymap fallthrough.
4904
- var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) {
4905
- function lookup(map) {
4906
- map = getKeyMap(map);
4907
- var found = map[name];
4908
- if (found === false) return "stop";
4909
- if (found != null && handle(found)) return true;
4910
- if (map.nofallthrough) return "stop";
4911
-
4912
- var fallthrough = map.fallthrough;
4913
- if (fallthrough == null) return false;
4914
- if (Object.prototype.toString.call(fallthrough) != "[object Array]")
4915
- return lookup(fallthrough);
4916
- for (var i = 0; i < fallthrough.length; ++i) {
4917
- var done = lookup(fallthrough[i]);
4918
- if (done) return done;
4907
+ function normalizeKeyName(name) {
4908
+ var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
4909
+ var alt, ctrl, shift, cmd;
4910
+ for (var i = 0; i < parts.length - 1; i++) {
4911
+ var mod = parts[i];
4912
+ if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
4913
+ else if (/^a(lt)?$/i.test(mod)) alt = true;
4914
+ else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
4915
+ else if (/^s(hift)$/i.test(mod)) shift = true;
4916
+ else throw new Error("Unrecognized modifier name: " + mod);
4917
+ }
4918
+ if (alt) name = "Alt-" + name;
4919
+ if (ctrl) name = "Ctrl-" + name;
4920
+ if (cmd) name = "Cmd-" + name;
4921
+ if (shift) name = "Shift-" + name;
4922
+ return name;
4923
+ }
4924
+
4925
+ // This is a kludge to keep keymaps mostly working as raw objects
4926
+ // (backwards compatibility) while at the same time support features
4927
+ // like normalization and multi-stroke key bindings. It compiles a
4928
+ // new normalized keymap, and then updates the old object to reflect
4929
+ // this.
4930
+ CodeMirror.normalizeKeyMap = function(keymap) {
4931
+ var copy = {};
4932
+ for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
4933
+ var value = keymap[keyname];
4934
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
4935
+ if (value == "...") { delete keymap[keyname]; continue; }
4936
+
4937
+ var keys = map(keyname.split(" "), normalizeKeyName);
4938
+ for (var i = 0; i < keys.length; i++) {
4939
+ var val, name;
4940
+ if (i == keys.length - 1) {
4941
+ name = keyname;
4942
+ val = value;
4943
+ } else {
4944
+ name = keys.slice(0, i + 1).join(" ");
4945
+ val = "...";
4946
+ }
4947
+ var prev = copy[name];
4948
+ if (!prev) copy[name] = val;
4949
+ else if (prev != val) throw new Error("Inconsistent bindings for " + name);
4919
4950
  }
4920
- return false;
4951
+ delete keymap[keyname];
4921
4952
  }
4953
+ for (var prop in copy) keymap[prop] = copy[prop];
4954
+ return keymap;
4955
+ };
4922
4956
 
4923
- for (var i = 0; i < maps.length; ++i) {
4924
- var done = lookup(maps[i]);
4925
- if (done) return done != "stop";
4957
+ var lookupKey = CodeMirror.lookupKey = function(key, map, handle) {
4958
+ map = getKeyMap(map);
4959
+ var found = map.call ? map.call(key) : map[key];
4960
+ if (found === false) return "nothing";
4961
+ if (found === "...") return "multi";
4962
+ if (found != null && handle(found)) return "handled";
4963
+
4964
+ if (map.fallthrough) {
4965
+ if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
4966
+ return lookupKey(key, map.fallthrough, handle);
4967
+ for (var i = 0; i < map.fallthrough.length; i++) {
4968
+ var result = lookupKey(key, map.fallthrough[i], handle);
4969
+ if (result) return result;
4970
+ }
4926
4971
  }
4927
4972
  };
4928
4973
 
4929
4974
  // Modifier key presses don't count as 'real' key presses for the
4930
4975
  // purpose of keymap fallthrough.
4931
- var isModifierKey = CodeMirror.isModifierKey = function(event) {
4932
- var name = keyNames[event.keyCode];
4976
+ var isModifierKey = CodeMirror.isModifierKey = function(value) {
4977
+ var name = typeof value == "string" ? value : keyNames[value.keyCode];
4933
4978
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
4934
4979
  };
4935
4980
 
4936
4981
  // Look up the name of a key as indicated by an event object.
4937
4982
  var keyName = CodeMirror.keyName = function(event, noShift) {
4938
4983
  if (presto && event.keyCode == 34 && event["char"]) return false;
4939
- var name = keyNames[event.keyCode];
4984
+ var base = keyNames[event.keyCode], name = base;
4940
4985
  if (name == null || event.altGraphKey) return false;
4941
- if (event.altKey) name = "Alt-" + name;
4942
- if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
4943
- if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
4944
- if (!noShift && event.shiftKey) name = "Shift-" + name;
4986
+ if (event.altKey && base != "Alt") name = "Alt-" + name;
4987
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
4988
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
4989
+ if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
4945
4990
  return name;
4946
4991
  };
4947
4992
 
4993
+ function getKeyMap(val) {
4994
+ return typeof val == "string" ? keyMap[val] : val;
4995
+ }
4996
+
4948
4997
  // FROMTEXTAREA
4949
4998
 
4950
4999
  CodeMirror.fromTextArea = function(textarea, options) {
@@ -5794,20 +5843,44 @@
5794
5843
  if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
5795
5844
  }
5796
5845
 
5797
- function readToken(mode, stream, state) {
5846
+ function readToken(mode, stream, state, inner) {
5798
5847
  for (var i = 0; i < 10; i++) {
5848
+ if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
5799
5849
  var style = mode.token(stream, state);
5800
5850
  if (stream.pos > stream.start) return style;
5801
5851
  }
5802
5852
  throw new Error("Mode " + mode.name + " failed to advance stream.");
5803
5853
  }
5804
5854
 
5855
+ // Utility for getTokenAt and getLineTokens
5856
+ function takeToken(cm, pos, precise, asArray) {
5857
+ function getObj(copy) {
5858
+ return {start: stream.start, end: stream.pos,
5859
+ string: stream.current(),
5860
+ type: style || null,
5861
+ state: copy ? copyState(doc.mode, state) : state};
5862
+ }
5863
+
5864
+ var doc = cm.doc, mode = doc.mode, style;
5865
+ pos = clipPos(doc, pos);
5866
+ var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
5867
+ var stream = new StringStream(line.text, cm.options.tabSize), tokens;
5868
+ if (asArray) tokens = [];
5869
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
5870
+ stream.start = stream.pos;
5871
+ style = readToken(mode, stream, state);
5872
+ if (asArray) tokens.push(getObj(true));
5873
+ }
5874
+ return asArray ? tokens : getObj();
5875
+ }
5876
+
5805
5877
  // Run the given mode's parser over a line, calling f for each token.
5806
5878
  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
5807
5879
  var flattenSpans = mode.flattenSpans;
5808
5880
  if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
5809
5881
  var curStart = 0, curStyle = null;
5810
5882
  var stream = new StringStream(text, cm.options.tabSize), style;
5883
+ var inner = cm.options.addModeClass && [null];
5811
5884
  if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
5812
5885
  while (!stream.eol()) {
5813
5886
  if (stream.pos > cm.options.maxHighlightLength) {
@@ -5816,10 +5889,10 @@
5816
5889
  stream.pos = text.length;
5817
5890
  style = null;
5818
5891
  } else {
5819
- style = extractLineClasses(readToken(mode, stream, state), lineClasses);
5892
+ style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
5820
5893
  }
5821
- if (cm.options.addModeClass) {
5822
- var mName = CodeMirror.innerMode(mode, state).mode.name;
5894
+ if (inner) {
5895
+ var mName = inner[0].name;
5823
5896
  if (mName) style = "m-" + (style ? mName + " " + style : mName);
5824
5897
  }
5825
5898
  if (!flattenSpans || curStyle != style) {
@@ -5878,12 +5951,13 @@
5878
5951
  return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
5879
5952
  }
5880
5953
 
5881
- function getLineStyles(cm, line) {
5954
+ function getLineStyles(cm, line, updateFrontier) {
5882
5955
  if (!line.styles || line.styles[0] != cm.state.modeGen) {
5883
5956
  var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
5884
5957
  line.styles = result.styles;
5885
5958
  if (result.classes) line.styleClasses = result.classes;
5886
5959
  else if (line.styleClasses) line.styleClasses = null;
5960
+ if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
5887
5961
  }
5888
5962
  return line.styles;
5889
5963
  }
@@ -5938,7 +6012,8 @@
5938
6012
  if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
5939
6013
  builder.addToken = buildTokenBadBidi(builder.addToken, order);
5940
6014
  builder.map = [];
5941
- insertLineContent(line, builder, getLineStyles(cm, line));
6015
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6016
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
5942
6017
  if (line.styleClasses) {
5943
6018
  if (line.styleClasses.bgClass)
5944
6019
  builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
@@ -5960,9 +6035,14 @@
5960
6035
  }
5961
6036
  }
5962
6037
 
6038
+ // See issue #2901
6039
+ if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6040
+ builder.content.className = "cm-tab-wrap-hack";
6041
+
5963
6042
  signal(cm, "renderLine", cm, lineView.line, builder.pre);
5964
6043
  if (builder.pre.className)
5965
6044
  builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6045
+
5966
6046
  return builder;
5967
6047
  }
5968
6048
 
@@ -6531,22 +6611,26 @@
6531
6611
  },
6532
6612
 
6533
6613
  addLineClass: docMethodOp(function(handle, where, cls) {
6534
- return changeLine(this, handle, "class", function(line) {
6535
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
6614
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
6615
+ var prop = where == "text" ? "textClass"
6616
+ : where == "background" ? "bgClass"
6617
+ : where == "gutter" ? "gutterClass" : "wrapClass";
6536
6618
  if (!line[prop]) line[prop] = cls;
6537
- else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
6619
+ else if (classTest(cls).test(line[prop])) return false;
6538
6620
  else line[prop] += " " + cls;
6539
6621
  return true;
6540
6622
  });
6541
6623
  }),
6542
6624
  removeLineClass: docMethodOp(function(handle, where, cls) {
6543
6625
  return changeLine(this, handle, "class", function(line) {
6544
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
6626
+ var prop = where == "text" ? "textClass"
6627
+ : where == "background" ? "bgClass"
6628
+ : where == "gutter" ? "gutterClass" : "wrapClass";
6545
6629
  var cur = line[prop];
6546
6630
  if (!cur) return false;
6547
6631
  else if (cls == null) line[prop] = null;
6548
6632
  else {
6549
- var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
6633
+ var found = cur.match(classTest(cls));
6550
6634
  if (!found) return false;
6551
6635
  var end = found.index + found[0].length;
6552
6636
  line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
@@ -7165,6 +7249,8 @@
7165
7249
  // registering a (non-DOM) handler on the editor for the event name,
7166
7250
  // and preventDefault-ing the event in that handler.
7167
7251
  function signalDOMEvent(cm, e, override) {
7252
+ if (typeof e == "string")
7253
+ e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
7168
7254
  signal(cm, override || e.type, cm, e);
7169
7255
  return e_defaultPrevented(e) || e.codemirrorIgnore;
7170
7256
  }
@@ -7338,7 +7424,8 @@
7338
7424
  };
7339
7425
  else range = function(node, start, end) {
7340
7426
  var r = document.body.createTextRange();
7341
- r.moveToElementText(node.parentNode);
7427
+ try { r.moveToElementText(node.parentNode); }
7428
+ catch(e) { return r; }
7342
7429
  r.collapse(true);
7343
7430
  r.moveEnd("character", end);
7344
7431
  r.moveStart("character", start);
@@ -7370,14 +7457,19 @@
7370
7457
  catch(e) { return document.body; }
7371
7458
  };
7372
7459
 
7373
- function classTest(cls) { return new RegExp("\\b" + cls + "\\b\\s*"); }
7374
- function rmClass(node, cls) {
7375
- var test = classTest(cls);
7376
- if (test.test(node.className)) node.className = node.className.replace(test, "");
7377
- }
7378
- function addClass(node, cls) {
7379
- if (!classTest(cls).test(node.className)) node.className += " " + cls;
7380
- }
7460
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
7461
+ var rmClass = CodeMirror.rmClass = function(node, cls) {
7462
+ var current = node.className;
7463
+ var match = classTest(cls).exec(current);
7464
+ if (match) {
7465
+ var after = current.slice(match.index + match[0].length);
7466
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
7467
+ }
7468
+ };
7469
+ var addClass = CodeMirror.addClass = function(node, cls) {
7470
+ var current = node.className;
7471
+ if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
7472
+ };
7381
7473
  function joinClasses(a, b) {
7382
7474
  var as = a.split(" ");
7383
7475
  for (var i = 0; i < as.length; i++)
@@ -7824,7 +7916,7 @@
7824
7916
 
7825
7917
  // THE END
7826
7918
 
7827
- CodeMirror.version = "4.7.0";
7919
+ CodeMirror.version = "4.8.0";
7828
7920
 
7829
7921
  return CodeMirror;
7830
7922
  });