codemirror-rails 2.2.1 → 2.3

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 (81) hide show
  1. data/README.md +8 -0
  2. data/codemirror-rails.gemspec +1 -1
  3. data/lib/codemirror/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/codemirror.js +842 -422
  5. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +2 -2
  6. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +722 -32
  7. data/vendor/assets/javascripts/codemirror/modes/clike.js +31 -9
  8. data/vendor/assets/javascripts/codemirror/modes/clojure.js +14 -14
  9. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +59 -37
  10. data/vendor/assets/javascripts/codemirror/modes/css.js +1 -1
  11. data/vendor/assets/javascripts/codemirror/modes/diff.js +24 -5
  12. data/vendor/assets/javascripts/codemirror/modes/ecl.js +203 -0
  13. data/vendor/assets/javascripts/codemirror/modes/erlang.js +251 -0
  14. data/vendor/assets/javascripts/codemirror/modes/gfm.js +40 -4
  15. data/vendor/assets/javascripts/codemirror/modes/go.js +170 -0
  16. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +1 -1
  17. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +4 -2
  18. data/vendor/assets/javascripts/codemirror/modes/javascript.js +10 -9
  19. data/vendor/assets/javascripts/codemirror/modes/less.js +232 -0
  20. data/vendor/assets/javascripts/codemirror/modes/markdown.js +52 -49
  21. data/vendor/assets/javascripts/codemirror/modes/mysql.js +188 -0
  22. data/vendor/assets/javascripts/codemirror/modes/pascal.js +2 -46
  23. data/vendor/assets/javascripts/codemirror/modes/perl.js +1 -1
  24. data/vendor/assets/javascripts/codemirror/modes/php.js +55 -25
  25. data/vendor/assets/javascripts/codemirror/modes/pig.js +172 -0
  26. data/vendor/assets/javascripts/codemirror/modes/properties.js +63 -0
  27. data/vendor/assets/javascripts/codemirror/modes/python.js +37 -32
  28. data/vendor/assets/javascripts/codemirror/modes/rpm-spec.js +1 -1
  29. data/vendor/assets/javascripts/codemirror/modes/rst.js +1 -1
  30. data/vendor/assets/javascripts/codemirror/modes/ruby.js +14 -14
  31. data/vendor/assets/javascripts/codemirror/modes/rust.js +36 -15
  32. data/vendor/assets/javascripts/codemirror/modes/scheme.js +74 -46
  33. data/vendor/assets/javascripts/codemirror/modes/shell.js +103 -0
  34. data/vendor/assets/javascripts/codemirror/modes/smalltalk.js +16 -16
  35. data/vendor/assets/javascripts/codemirror/modes/smarty.js +148 -0
  36. data/vendor/assets/javascripts/codemirror/modes/stex.js +21 -6
  37. data/vendor/assets/javascripts/codemirror/modes/tiddlywiki.js +55 -45
  38. data/vendor/assets/javascripts/codemirror/modes/tiki.js +316 -0
  39. data/vendor/assets/javascripts/codemirror/modes/vbscript.js +26 -0
  40. data/vendor/assets/javascripts/codemirror/modes/verilog.js +194 -0
  41. data/vendor/assets/javascripts/codemirror/modes/xml.js +89 -16
  42. data/vendor/assets/javascripts/codemirror/modes/xmlpure.js +5 -0
  43. data/vendor/assets/javascripts/codemirror/modes/xquery.js +448 -0
  44. data/vendor/assets/javascripts/codemirror/utils/closetag.js +146 -0
  45. data/vendor/assets/javascripts/codemirror/utils/dialog.js +63 -0
  46. data/vendor/assets/javascripts/codemirror/utils/foldcode.js +196 -0
  47. data/vendor/assets/javascripts/codemirror/utils/formatting.js +297 -0
  48. data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +134 -0
  49. data/vendor/assets/javascripts/codemirror/utils/loadmode.js +51 -0
  50. data/vendor/assets/javascripts/codemirror/utils/match-highlighter.js +44 -0
  51. data/vendor/assets/javascripts/codemirror/utils/multiplex.js +72 -0
  52. data/vendor/assets/javascripts/codemirror/{overlay.js → utils/overlay.js} +3 -2
  53. data/vendor/assets/javascripts/codemirror/utils/pig-hint.js +123 -0
  54. data/vendor/assets/javascripts/codemirror/utils/runmode.js +49 -0
  55. data/vendor/assets/javascripts/codemirror/utils/search.js +118 -0
  56. data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +117 -0
  57. data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +72 -0
  58. data/vendor/assets/stylesheets/codemirror.css +69 -5
  59. data/vendor/assets/stylesheets/codemirror/modes/tiddlywiki.css +14 -21
  60. data/vendor/assets/stylesheets/codemirror/modes/tiki.css +26 -0
  61. data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +81 -0
  62. data/vendor/assets/stylesheets/codemirror/themes/blackboard.css +25 -0
  63. data/vendor/assets/stylesheets/codemirror/themes/cobalt.css +1 -1
  64. data/vendor/assets/stylesheets/codemirror/themes/eclipse.css +1 -1
  65. data/vendor/assets/stylesheets/codemirror/themes/elegant.css +2 -2
  66. data/vendor/assets/stylesheets/codemirror/themes/erlang-dark.css +21 -0
  67. data/vendor/assets/stylesheets/codemirror/themes/lesser-dark.css +44 -0
  68. data/vendor/assets/stylesheets/codemirror/themes/monokai.css +1 -1
  69. data/vendor/assets/stylesheets/codemirror/themes/neat.css +3 -3
  70. data/vendor/assets/stylesheets/codemirror/themes/night.css +1 -1
  71. data/vendor/assets/stylesheets/codemirror/themes/rubyblue.css +2 -2
  72. data/vendor/assets/stylesheets/codemirror/themes/vibrant-ink.css +27 -0
  73. data/vendor/assets/stylesheets/codemirror/themes/xq-dark.css +46 -0
  74. data/vendor/assets/stylesheets/codemirror/utils/dialog.css +23 -0
  75. data/vendor/assets/stylesheets/codemirror/utils/simple-hint.css +16 -0
  76. metadata +41 -10
  77. data/vendor/assets/javascripts/codemirror/runmode.js +0 -27
  78. data/vendor/assets/stylesheets/codemirror/modes/clike.css +0 -7
  79. data/vendor/assets/stylesheets/codemirror/modes/markdown.css +0 -10
  80. data/vendor/assets/stylesheets/codemirror/modes/rst.css +0 -75
  81. data/vendor/assets/stylesheets/codemirror/themes/default.css +0 -19
data/README.md CHANGED
@@ -35,6 +35,14 @@ Additional syntax modes can be added to your application.js:
35
35
  //= require codemirror/modes/ruby
36
36
  ```
37
37
 
38
+ ### Adding a util
39
+
40
+ Additional reusable util components can be added in your application.js:
41
+
42
+ ```js
43
+ //= require codemirror/utils/dialog
44
+ ```
45
+
38
46
  ### Adding a keymap
39
47
 
40
48
  Additional keymap bindings can be added to your application.js:
@@ -3,7 +3,7 @@ require File.expand_path('../lib/codemirror/rails/version', __FILE__)
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'codemirror-rails'
5
5
  s.version = Codemirror::Rails::VERSION
6
- s.authors = ['Nathan Fixler']
6
+ s.authors = ['Nathan Fixler', 'Robin Bühler']
7
7
  s.email = 'nathan@fixler.org'
8
8
  s.summary = 'Use CodeMirror with Rails 3'
9
9
  s.description = 'This gem provides CodeMirror assets for your Rails 3 application.'
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '2.2.1'
4
- CODEMIRROR_VERSION = '2.2'
3
+ VERSION = '2.3'
4
+ CODEMIRROR_VERSION = '2.3'
5
5
  end
6
6
  end
@@ -1,4 +1,4 @@
1
- // CodeMirror version 2.2
1
+ // CodeMirror version 2.3
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,
@@ -6,7 +6,7 @@
6
6
 
7
7
  // CodeMirror is the only global var we claim
8
8
  var CodeMirror = (function() {
9
- // This is the function that produces an editor instance. It's
9
+ // This is the function that produces an editor instance. Its
10
10
  // closure is used to store the editor state.
11
11
  function CodeMirror(place, givenOptions) {
12
12
  // Determine effective options based on given values and defaults.
@@ -15,24 +15,28 @@ var CodeMirror = (function() {
15
15
  if (defaults.hasOwnProperty(opt))
16
16
  options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
17
17
 
18
- var targetDocument = options["document"];
19
18
  // The element in which the editor lives.
20
- var wrapper = targetDocument.createElement("div");
19
+ var wrapper = document.createElement("div");
21
20
  wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
22
21
  // This mess creates the base DOM structure for the editor.
23
22
  wrapper.innerHTML =
24
23
  '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
25
- '<textarea style="position: absolute; padding: 0; width: 1px;" wrap="off" ' +
24
+ '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
26
25
  'autocorrect="off" autocapitalize="off"></textarea></div>' +
26
+ '<div class="CodeMirror-scrollbar">' + // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
27
+ '<div class="CodeMirror-scrollbar-inner">' + // The empty scrollbar content, used solely for managing the scrollbar thumb.
28
+ '</div></div>' + // This must be before the scroll area because it's float-right.
27
29
  '<div class="CodeMirror-scroll" tabindex="-1">' +
28
30
  '<div style="position: relative">' + // Set to the height of the text, causes scrolling
29
31
  '<div style="position: relative">' + // Moved around its parent to cover visible view
30
32
  '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
31
33
  // Provides positioning relative to (visible) text origin
32
- '<div class="CodeMirror-lines"><div style="position: relative">' +
33
- '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
34
+ '<div class="CodeMirror-lines"><div style="position: relative; z-index: 0">' +
35
+ // Used to measure text size
36
+ '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden;"></div>' +
34
37
  '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
35
- '<div></div>' + // This DIV contains the actual code
38
+ '<pre class="CodeMirror-cursor" style="visibility: hidden">&#160;</pre>' + // Used to force a width
39
+ '<div style="position: relative; z-index: -1"></div><div></div>' + // DIVs containing the selection and the actual code
36
40
  '</div></div></div></div></div>';
37
41
  if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
38
42
  // I've never seen more elegant code in my life.
@@ -40,13 +44,30 @@ var CodeMirror = (function() {
40
44
  scroller = wrapper.lastChild, code = scroller.firstChild,
41
45
  mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
42
46
  lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
43
- cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
44
- themeChanged();
47
+ cursor = measure.nextSibling, widthForcer = cursor.nextSibling,
48
+ selectionDiv = widthForcer.nextSibling, lineDiv = selectionDiv.nextSibling,
49
+ scrollbar = inputDiv.nextSibling, scrollbarInner = scrollbar.firstChild;
50
+ themeChanged(); keyMapChanged();
45
51
  // Needed to hide big blue blinking cursor on Mobile Safari
46
- if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
47
- if (!webkit) lineSpace.draggable = true;
52
+ if (ios) input.style.width = "0px";
53
+ if (!webkit) scroller.draggable = true;
54
+ lineSpace.style.outline = "none";
48
55
  if (options.tabindex != null) input.tabIndex = options.tabindex;
56
+ if (options.autofocus) focusInput();
49
57
  if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
58
+ // Needed to handle Tab key in KHTML
59
+ if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
60
+
61
+ // Check for OS X >= 10.7. If so, we need to force a width on the scrollbar, and
62
+ // make it overlap the content. (But we only do this if the scrollbar doesn't already
63
+ // have a natural width. If the mouse is plugged in or the user sets the system pref
64
+ // to always show scrollbars, the scrollbar shouldn't overlap.)
65
+ if (mac_geLion) {
66
+ scrollbar.className += (overlapScrollbars() ? " cm-sb-overlap" : " cm-sb-nonoverlap");
67
+ } else if (ie_lt8) {
68
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
69
+ scrollbar.className += " cm-sb-ie7";
70
+ }
50
71
 
51
72
  // Check for problem with IE innerHTML not working when we have a
52
73
  // P (or similar) parent node.
@@ -71,19 +92,21 @@ var CodeMirror = (function() {
71
92
  var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
72
93
  // Selection-related flags. shiftSelecting obviously tracks
73
94
  // whether the user is holding shift.
74
- var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
95
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText,
96
+ overwrite = false, suppressEdits = false;
75
97
  // Variables used by startOperation/endOperation to track what
76
98
  // happened during the operation.
77
99
  var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
78
100
  gutterDirty, callbacks;
79
101
  // Current visible range (may be bigger than the view window).
80
102
  var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
81
- // bracketHighlighted is used to remember that a backet has been
103
+ // bracketHighlighted is used to remember that a bracket has been
82
104
  // marked.
83
105
  var bracketHighlighted;
84
106
  // Tracks the maximum line length so that the horizontal scrollbar
85
107
  // can be kept static when scrolling.
86
- var maxLine = "", maxWidth, tabText = computeTabText();
108
+ var maxLine = "", updateMaxLine = false, maxLineChanged = true;
109
+ var tabCache = {};
87
110
 
88
111
  // Initialize the content.
89
112
  operation(function(){setValue(options.value || ""); updateInput = false;})();
@@ -92,17 +115,16 @@ var CodeMirror = (function() {
92
115
  // Register our event handlers.
93
116
  connect(scroller, "mousedown", operation(onMouseDown));
94
117
  connect(scroller, "dblclick", operation(onDoubleClick));
95
- connect(lineSpace, "dragstart", onDragStart);
96
118
  connect(lineSpace, "selectstart", e_preventDefault);
97
119
  // Gecko browsers fire contextmenu *after* opening the menu, at
98
120
  // which point we can't mess with it anymore. Context menu is
99
121
  // handled in onMouseDown for Gecko.
100
122
  if (!gecko) connect(scroller, "contextmenu", onContextMenu);
101
- connect(scroller, "scroll", function() {
102
- updateDisplay([]);
103
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
104
- if (options.onScroll) options.onScroll(instance);
105
- });
123
+ connect(scroller, "scroll", onScroll);
124
+ connect(scrollbar, "scroll", onScroll);
125
+ connect(scrollbar, "mousedown", function() {setTimeout(focusInput, 0);});
126
+ connect(scroller, "mousewheel", onMouseWheel);
127
+ connect(scroller, "DOMMouseScroll", onMouseWheel);
106
128
  connect(window, "resize", function() {updateDisplay(true);});
107
129
  connect(input, "keyup", operation(onKeyUp));
108
130
  connect(input, "input", fastPoll);
@@ -111,17 +133,32 @@ var CodeMirror = (function() {
111
133
  connect(input, "focus", onFocus);
112
134
  connect(input, "blur", onBlur);
113
135
 
114
- connect(scroller, "dragenter", e_stop);
115
- connect(scroller, "dragover", e_stop);
116
- connect(scroller, "drop", operation(onDrop));
136
+ if (options.dragDrop) {
137
+ connect(scroller, "dragstart", onDragStart);
138
+ function drag_(e) {
139
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
140
+ e_stop(e);
141
+ }
142
+ connect(scroller, "dragenter", drag_);
143
+ connect(scroller, "dragover", drag_);
144
+ connect(scroller, "drop", operation(onDrop));
145
+ }
117
146
  connect(scroller, "paste", function(){focusInput(); fastPoll();});
118
147
  connect(input, "paste", fastPoll);
119
- connect(input, "cut", operation(function(){replaceSelection("");}));
148
+ connect(input, "cut", operation(function(){
149
+ if (!options.readOnly) replaceSelection("");
150
+ }));
151
+
152
+ // Needed to handle Tab key in KHTML
153
+ if (khtml) connect(code, "mouseup", function() {
154
+ if (document.activeElement == input) input.blur();
155
+ focusInput();
156
+ });
120
157
 
121
158
  // IE throws unspecified error in certain cases, when
122
159
  // trying to access activeElement before onload
123
- var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
124
- if (hasFocus) setTimeout(onFocus, 20);
160
+ var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { }
161
+ if (hasFocus || options.autofocus) setTimeout(onFocus, 20);
125
162
  else onBlur();
126
163
 
127
164
  function isLine(l) {return l >= 0 && l < doc.size;}
@@ -135,23 +172,31 @@ var CodeMirror = (function() {
135
172
  setValue: operation(setValue),
136
173
  getSelection: getSelection,
137
174
  replaceSelection: operation(replaceSelection),
138
- focus: function(){focusInput(); onFocus(); fastPoll();},
175
+ focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
139
176
  setOption: function(option, value) {
140
177
  var oldVal = options[option];
141
178
  options[option] = value;
142
179
  if (option == "mode" || option == "indentUnit") loadMode();
143
- else if (option == "readOnly" && value) {onBlur(); input.blur();}
180
+ else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
181
+ else if (option == "readOnly" && !value) {resetInput(true);}
144
182
  else if (option == "theme") themeChanged();
145
183
  else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
146
- else if (option == "tabSize") operation(tabsChanged)();
147
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
148
- operation(gutterChanged)();
184
+ else if (option == "tabSize") updateDisplay(true);
185
+ else if (option == "keyMap") keyMapChanged();
186
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
187
+ gutterChanged();
188
+ updateDisplay(true);
189
+ }
149
190
  },
150
191
  getOption: function(option) {return options[option];},
151
192
  undo: operation(undo),
152
193
  redo: operation(redo),
153
194
  indentLine: operation(function(n, dir) {
154
- if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
195
+ if (typeof dir != "string") {
196
+ if (dir == null) dir = options.smartIndent ? "smart" : "prev";
197
+ else dir = dir ? "add" : "subtract";
198
+ }
199
+ if (isLine(n)) indentLine(n, dir);
155
200
  }),
156
201
  indentSelection: operation(indentSelected),
157
202
  historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
@@ -165,17 +210,23 @@ var CodeMirror = (function() {
165
210
  line = clipLine(line == null ? doc.size - 1: line);
166
211
  return getStateBefore(line + 1);
167
212
  },
168
- cursorCoords: function(start){
213
+ cursorCoords: function(start, mode) {
169
214
  if (start == null) start = sel.inverted;
170
- return pageCoords(start ? sel.from : sel.to);
215
+ return this.charCoords(start ? sel.from : sel.to, mode);
216
+ },
217
+ charCoords: function(pos, mode) {
218
+ pos = clipPos(pos);
219
+ if (mode == "local") return localCoords(pos, false);
220
+ if (mode == "div") return localCoords(pos, true);
221
+ return pageCoords(pos);
171
222
  },
172
- charCoords: function(pos){return pageCoords(clipPos(pos));},
173
223
  coordsChar: function(coords) {
174
224
  var off = eltOffset(lineSpace);
175
225
  return coordsChar(coords.x - off.left, coords.y - off.top);
176
226
  },
177
227
  markText: operation(markText),
178
228
  setBookmark: setBookmark,
229
+ findMarksAt: findMarksAt,
179
230
  setMarker: operation(addGutterMarker),
180
231
  clearMarker: operation(removeGutterMarker),
181
232
  setLineClass: operation(setLineClass),
@@ -243,12 +294,21 @@ var CodeMirror = (function() {
243
294
  replaceRange: operation(replaceRange),
244
295
  getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
245
296
 
297
+ triggerOnKeyDown: operation(onKeyDown),
246
298
  execCommand: function(cmd) {return commands[cmd](instance);},
247
299
  // Stuff used by commands, probably not much use to outside code.
248
300
  moveH: operation(moveH),
249
301
  deleteH: operation(deleteH),
250
302
  moveV: operation(moveV),
251
- toggleOverwrite: function() {overwrite = !overwrite;},
303
+ toggleOverwrite: function() {
304
+ if(overwrite){
305
+ overwrite = false;
306
+ cursor.className = cursor.className.replace(" CodeMirror-overwrite", "");
307
+ } else {
308
+ overwrite = true;
309
+ cursor.className += " CodeMirror-overwrite";
310
+ }
311
+ },
252
312
 
253
313
  posFromIndex: function(off) {
254
314
  var lineNo = 0, ch;
@@ -268,9 +328,23 @@ var CodeMirror = (function() {
268
328
  });
269
329
  return index;
270
330
  },
331
+ scrollTo: function(x, y) {
332
+ if (x != null) scroller.scrollLeft = x;
333
+ if (y != null) scrollbar.scrollTop = y;
334
+ updateDisplay([]);
335
+ },
336
+ getScrollInfo: function() {
337
+ return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
338
+ height: scrollbar.scrollHeight, width: scroller.scrollWidth};
339
+ },
271
340
 
272
341
  operation: function(f){return operation(f)();},
273
- refresh: function(){updateDisplay(true);},
342
+ compoundChange: function(f){return compoundChange(f);},
343
+ refresh: function(){
344
+ updateDisplay(true);
345
+ if (scrollbar.scrollHeight > lastScrollTop)
346
+ scrollbar.scrollTop = lastScrollTop;
347
+ },
274
348
  getInputField: function(){return input;},
275
349
  getWrapperElement: function(){return wrapper;},
276
350
  getScrollerElement: function(){return scroller;},
@@ -290,14 +364,24 @@ var CodeMirror = (function() {
290
364
  splitLines(code), top, top);
291
365
  updateInput = true;
292
366
  }
293
- function getValue(code) {
367
+ function getValue() {
294
368
  var text = [];
295
369
  doc.iter(0, doc.size, function(line) { text.push(line.text); });
296
370
  return text.join("\n");
297
371
  }
298
372
 
373
+ function onScroll(e) {
374
+ if (lastScrollTop != scrollbar.scrollTop || lastScrollLeft != scroller.scrollLeft) {
375
+ lastScrollTop = scrollbar.scrollTop;
376
+ lastScrollLeft = scroller.scrollLeft;
377
+ updateDisplay([]);
378
+ if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
379
+ if (options.onScroll) options.onScroll(instance);
380
+ }
381
+ }
382
+
299
383
  function onMouseDown(e) {
300
- setShift(e.shiftKey);
384
+ setShift(e_prop(e, "shiftKey"));
301
385
  // Check whether this is a click in a widget
302
386
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
303
387
  if (n.parentNode == code && n != mover) return;
@@ -318,6 +402,8 @@ var CodeMirror = (function() {
318
402
  return;
319
403
  case 2:
320
404
  if (start) setCursor(start.line, start.ch, true);
405
+ setTimeout(focusInput, 20);
406
+ e_preventDefault(e);
321
407
  return;
322
408
  }
323
409
  // For button 1, if it was clicked inside the editor
@@ -339,21 +425,25 @@ var CodeMirror = (function() {
339
425
  } else { lastClick = {time: now, pos: start}; }
340
426
 
341
427
  var last = start, going;
342
- if (dragAndDrop && !posEq(sel.from, sel.to) &&
428
+ if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
343
429
  !posLess(start, sel.from) && !posLess(sel.to, start)) {
344
430
  // Let the drag handler handle this.
345
- if (webkit) lineSpace.draggable = true;
346
- var up = connect(targetDocument, "mouseup", operation(function(e2) {
347
- if (webkit) lineSpace.draggable = false;
431
+ if (webkit) scroller.draggable = true;
432
+ function dragEnd(e2) {
433
+ if (webkit) scroller.draggable = false;
348
434
  draggingText = false;
349
- up();
435
+ up(); drop();
350
436
  if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
351
437
  e_preventDefault(e2);
352
438
  setCursor(start.line, start.ch, true);
353
439
  focusInput();
354
440
  }
355
- }), true);
441
+ }
442
+ var up = connect(document, "mouseup", operation(dragEnd), true);
443
+ var drop = connect(scroller, "drop", operation(dragEnd), true);
356
444
  draggingText = true;
445
+ // IE's approach to draggable
446
+ if (scroller.dragDrop) scroller.dragDrop();
357
447
  return;
358
448
  }
359
449
  e_preventDefault(e);
@@ -372,12 +462,7 @@ var CodeMirror = (function() {
372
462
  }
373
463
  }
374
464
 
375
- var move = connect(targetDocument, "mousemove", operation(function(e) {
376
- clearTimeout(going);
377
- e_preventDefault(e);
378
- extend(e);
379
- }), true);
380
- var up = connect(targetDocument, "mouseup", operation(function(e) {
465
+ function done(e) {
381
466
  clearTimeout(going);
382
467
  var cur = posFromMouse(e);
383
468
  if (cur) setSelectionUser(start, cur);
@@ -385,7 +470,14 @@ var CodeMirror = (function() {
385
470
  focusInput();
386
471
  updateInput = true;
387
472
  move(); up();
473
+ }
474
+ var move = connect(document, "mousemove", operation(function(e) {
475
+ clearTimeout(going);
476
+ e_preventDefault(e);
477
+ if (!ie && !e_button(e)) done(e);
478
+ else extend(e);
388
479
  }), true);
480
+ var up = connect(document, "mouseup", operation(done), true);
389
481
  }
390
482
  function onDoubleClick(e) {
391
483
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
@@ -397,6 +489,7 @@ var CodeMirror = (function() {
397
489
  selectWordAt(start);
398
490
  }
399
491
  function onDrop(e) {
492
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
400
493
  e.preventDefault();
401
494
  var pos = posFromMouse(e, true), files = e.dataTransfer.files;
402
495
  if (!pos || options.readOnly) return;
@@ -406,108 +499,156 @@ var CodeMirror = (function() {
406
499
  reader.onload = function() {
407
500
  text[i] = reader.result;
408
501
  if (++read == n) {
409
- pos = clipPos(pos);
410
- operation(function() {
502
+ pos = clipPos(pos);
503
+ operation(function() {
411
504
  var end = replaceRange(text.join(""), pos, pos);
412
505
  setSelectionUser(pos, end);
413
506
  })();
414
- }
507
+ }
415
508
  };
416
509
  reader.readAsText(file);
417
510
  }
418
511
  var n = files.length, text = Array(n), read = 0;
419
512
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
420
- }
421
- else {
513
+ } else {
514
+ // Don't do a replace if the drop happened inside of the selected text.
515
+ if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return;
422
516
  try {
423
517
  var text = e.dataTransfer.getData("Text");
424
518
  if (text) {
425
- var end = replaceRange(text, pos, pos);
426
- var curFrom = sel.from, curTo = sel.to;
427
- setSelectionUser(pos, end);
428
- if (draggingText) replaceRange("", curFrom, curTo);
429
- focusInput();
430
- }
519
+ compoundChange(function() {
520
+ var curFrom = sel.from, curTo = sel.to;
521
+ setSelectionUser(pos, pos);
522
+ if (draggingText) replaceRange("", curFrom, curTo);
523
+ replaceSelection(text);
524
+ focusInput();
525
+ });
526
+ }
431
527
  }
432
528
  catch(e){}
433
529
  }
434
530
  }
435
531
  function onDragStart(e) {
436
532
  var txt = getSelection();
437
- // This will reset escapeElement
438
- htmlEscape(txt);
439
- e.dataTransfer.setDragImage(escapeElement, 0, 0);
440
533
  e.dataTransfer.setData("Text", txt);
441
- }
442
- function handleKeyBinding(e) {
443
- var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
444
- if (name == null || e.altGraphKey) {
445
- if (next) options.keyMap = next;
446
- return null;
447
- }
448
- if (e.altKey) name = "Alt-" + name;
449
- if (e.ctrlKey) name = "Ctrl-" + name;
450
- if (e.metaKey) name = "Cmd-" + name;
451
- if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
452
- dropShift = true;
453
- } else {
454
- bound = lookupKey(name, options.extraKeys, options.keyMap);
534
+
535
+ // Use dummy image instead of default browsers image.
536
+ if (gecko || chrome || opera) {
537
+ var img = document.createElement('img');
538
+ img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
539
+ e.dataTransfer.setDragImage(img, 0, 0);
455
540
  }
541
+ }
542
+
543
+ function doHandleBinding(bound, dropShift) {
456
544
  if (typeof bound == "string") {
457
- if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
458
- else bound = null;
459
- }
460
- if (next && (bound || !isModifierKey(e))) options.keyMap = next;
461
- if (!bound) return false;
462
- if (dropShift) {
463
- var prevShift = shiftSelecting;
464
- shiftSelecting = null;
545
+ bound = commands[bound];
546
+ if (!bound) return false;
547
+ }
548
+ var prevShift = shiftSelecting;
549
+ try {
550
+ if (options.readOnly) suppressEdits = true;
551
+ if (dropShift) shiftSelecting = null;
465
552
  bound(instance);
553
+ } catch(e) {
554
+ if (e != Pass) throw e;
555
+ return false;
556
+ } finally {
466
557
  shiftSelecting = prevShift;
467
- } else bound(instance);
468
- e_preventDefault(e);
558
+ suppressEdits = false;
559
+ }
469
560
  return true;
470
561
  }
471
- var lastStoppedKey = null;
562
+ function handleKeyBinding(e) {
563
+ // Handle auto keymap transitions
564
+ var startMap = getKeyMap(options.keyMap), next = startMap.auto;
565
+ clearTimeout(maybeTransition);
566
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
567
+ if (getKeyMap(options.keyMap) == startMap) {
568
+ options.keyMap = (next.call ? next.call(null, instance) : next);
569
+ }
570
+ }, 50);
571
+
572
+ var name = keyNames[e_prop(e, "keyCode")], handled = false;
573
+ if (name == null || e.altGraphKey) return false;
574
+ if (e_prop(e, "altKey")) name = "Alt-" + name;
575
+ if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
576
+ if (e_prop(e, "metaKey")) name = "Cmd-" + name;
577
+
578
+ var stopped = false;
579
+ function stop() { stopped = true; }
580
+
581
+ if (e_prop(e, "shiftKey")) {
582
+ handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
583
+ function(b) {return doHandleBinding(b, true);}, stop)
584
+ || lookupKey(name, options.extraKeys, options.keyMap, function(b) {
585
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
586
+ }, stop);
587
+ } else {
588
+ handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop);
589
+ }
590
+ if (stopped) handled = false;
591
+ if (handled) {
592
+ e_preventDefault(e);
593
+ restartBlink();
594
+ if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
595
+ }
596
+ return handled;
597
+ }
598
+ function handleCharBinding(e, ch) {
599
+ var handled = lookupKey("'" + ch + "'", options.extraKeys,
600
+ options.keyMap, function(b) { return doHandleBinding(b, true); });
601
+ if (handled) {
602
+ e_preventDefault(e);
603
+ restartBlink();
604
+ }
605
+ return handled;
606
+ }
607
+
608
+ var lastStoppedKey = null, maybeTransition;
472
609
  function onKeyDown(e) {
473
610
  if (!focused) onFocus();
474
- var code = e.keyCode;
611
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
612
+ if (pollingFast) { if (readInput()) pollingFast = false; }
613
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
614
+ var code = e_prop(e, "keyCode");
475
615
  // IE does strange things with escape.
476
- if (ie && code == 27) { e.returnValue = false; }
477
- setShift(code == 16 || e.shiftKey);
616
+ setShift(code == 16 || e_prop(e, "shiftKey"));
478
617
  // First give onKeyEvent option a chance to handle this.
479
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
480
618
  var handled = handleKeyBinding(e);
481
- if (window.opera) {
482
- lastStoppedKey = handled ? e.keyCode : null;
619
+ if (opera) {
620
+ lastStoppedKey = handled ? code : null;
483
621
  // Opera has no cut event... we try to at least catch the key combo
484
- if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
622
+ if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
485
623
  replaceSelection("");
486
624
  }
487
625
  }
488
626
  function onKeyPress(e) {
489
- if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
627
+ if (pollingFast) readInput();
490
628
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
491
- if (window.opera && !e.which && handleKeyBinding(e)) return;
492
- if (options.electricChars && mode.electricChars) {
493
- var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
629
+ var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
630
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
631
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
632
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
633
+ if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
494
634
  if (mode.electricChars.indexOf(ch) > -1)
495
635
  setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
496
636
  }
637
+ if (handleCharBinding(e, ch)) return;
497
638
  fastPoll();
498
639
  }
499
640
  function onKeyUp(e) {
500
641
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
501
- if (e.keyCode == 16) shiftSelecting = null;
642
+ if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
502
643
  }
503
644
 
504
645
  function onFocus() {
505
- if (options.readOnly) return;
646
+ if (options.readOnly == "nocursor") return;
506
647
  if (!focused) {
507
648
  if (options.onFocus) options.onFocus(instance);
508
649
  focused = true;
509
- if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
510
- wrapper.className += " CodeMirror-focused";
650
+ if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
651
+ scroller.className += " CodeMirror-focused";
511
652
  if (!leaveInputAlone) resetInput(true);
512
653
  }
513
654
  slowPoll();
@@ -517,15 +658,56 @@ var CodeMirror = (function() {
517
658
  if (focused) {
518
659
  if (options.onBlur) options.onBlur(instance);
519
660
  focused = false;
520
- wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
661
+ if (bracketHighlighted)
662
+ operation(function(){
663
+ if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
664
+ })();
665
+ scroller.className = scroller.className.replace(" CodeMirror-focused", "");
521
666
  }
522
667
  clearInterval(blinker);
523
668
  setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
524
669
  }
525
670
 
671
+ function chopDelta(delta) {
672
+ // Make sure we always scroll a little bit for any nonzero delta.
673
+ if (delta > 0.0 && delta < 1.0) return 1;
674
+ else if (delta > -1.0 && delta < 0.0) return -1;
675
+ else return Math.round(delta);
676
+ }
677
+
678
+ function onMouseWheel(e) {
679
+ var deltaX = 0, deltaY = 0;
680
+ if (e.type == "DOMMouseScroll") { // Firefox
681
+ var delta = -e.detail * 8.0;
682
+ if (e.axis == e.HORIZONTAL_AXIS) deltaX = delta;
683
+ else if (e.axis == e.VERTICAL_AXIS) deltaY = delta;
684
+ } else if (e.wheelDeltaX !== undefined && e.wheelDeltaY !== undefined) { // WebKit
685
+ deltaX = e.wheelDeltaX / 3.0;
686
+ deltaY = e.wheelDeltaY / 3.0;
687
+ } else if (e.wheelDelta !== undefined) { // IE or Opera
688
+ deltaY = e.wheelDelta / 3.0;
689
+ }
690
+
691
+ var scrolled = false;
692
+ deltaX = chopDelta(deltaX);
693
+ deltaY = chopDelta(deltaY);
694
+ if ((deltaX > 0 && scroller.scrollLeft > 0) ||
695
+ (deltaX < 0 && scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth)) {
696
+ scroller.scrollLeft -= deltaX;
697
+ scrolled = true;
698
+ }
699
+ if ((deltaY > 0 && scrollbar.scrollTop > 0) ||
700
+ (deltaY < 0 && scrollbar.scrollTop + scrollbar.clientHeight < scrollbar.scrollHeight)) {
701
+ scrollbar.scrollTop -= deltaY;
702
+ scrolled = true;
703
+ }
704
+ if (scrolled) e_stop(e);
705
+ }
706
+
526
707
  // Replace the range from from to to by the strings in newText.
527
708
  // Afterwards, set the selection to selFrom, selTo.
528
709
  function updateLines(from, to, newText, selFrom, selTo) {
710
+ if (suppressEdits) return;
529
711
  if (history) {
530
712
  var old = [];
531
713
  doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
@@ -535,25 +717,29 @@ var CodeMirror = (function() {
535
717
  updateLinesNoUndo(from, to, newText, selFrom, selTo);
536
718
  }
537
719
  function unredoHelper(from, to) {
538
- var change = from.pop();
539
- if (change) {
720
+ if (!from.length) return;
721
+ var set = from.pop(), out = [];
722
+ for (var i = set.length - 1; i >= 0; i -= 1) {
723
+ var change = set[i];
540
724
  var replaced = [], end = change.start + change.added;
541
725
  doc.iter(change.start, end, function(line) { replaced.push(line.text); });
542
- to.push({start: change.start, added: change.old.length, old: replaced});
543
- var pos = clipPos({line: change.start + change.old.length - 1,
544
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
726
+ out.push({start: change.start, added: change.old.length, old: replaced});
727
+ var pos = {line: change.start + change.old.length - 1,
728
+ ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
545
729
  updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
546
- updateInput = true;
547
730
  }
731
+ updateInput = true;
732
+ to.push(out);
548
733
  }
549
734
  function undo() {unredoHelper(history.done, history.undone);}
550
735
  function redo() {unredoHelper(history.undone, history.done);}
551
736
 
552
737
  function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
738
+ if (suppressEdits) return;
553
739
  var recomputeMaxLength = false, maxLineLength = maxLine.length;
554
740
  if (!options.lineWrapping)
555
- doc.iter(from.line, to.line, function(line) {
556
- if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
741
+ doc.iter(from.line, to.line + 1, function(line) {
742
+ if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
557
743
  });
558
744
  if (from.line != to.line || newText.length > 1) gutterDirty = true;
559
745
 
@@ -600,29 +786,21 @@ var CodeMirror = (function() {
600
786
  doc.insert(from.line + 1, added);
601
787
  }
602
788
  if (options.lineWrapping) {
603
- var perLine = scroller.clientWidth / charWidth() - 3;
789
+ var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
604
790
  doc.iter(from.line, from.line + newText.length, function(line) {
605
791
  if (line.hidden) return;
606
792
  var guess = Math.ceil(line.text.length / perLine) || 1;
607
793
  if (guess != line.height) updateLineHeight(line, guess);
608
794
  });
609
795
  } else {
610
- doc.iter(from.line, i + newText.length, function(line) {
796
+ doc.iter(from.line, from.line + newText.length, function(line) {
611
797
  var l = line.text;
612
- if (l.length > maxLineLength) {
613
- maxLine = l; maxLineLength = l.length; maxWidth = null;
798
+ if (!line.hidden && l.length > maxLineLength) {
799
+ maxLine = l; maxLineLength = l.length; maxLineChanged = true;
614
800
  recomputeMaxLength = false;
615
801
  }
616
802
  });
617
- if (recomputeMaxLength) {
618
- maxLineLength = 0; maxLine = ""; maxWidth = null;
619
- doc.iter(0, doc.size, function(line) {
620
- var l = line.text;
621
- if (l.length > maxLineLength) {
622
- maxLineLength = l.length; maxLine = l;
623
- }
624
- });
625
- }
803
+ if (recomputeMaxLength) updateMaxLine = true;
626
804
  }
627
805
 
628
806
  // Add these lines to the work array, so that they will be
@@ -648,10 +826,49 @@ var CodeMirror = (function() {
648
826
 
649
827
  // Update the selection
650
828
  function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
651
- setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
829
+ setSelection(clipPos(selFrom), clipPos(selTo),
830
+ updateLine(sel.from.line), updateLine(sel.to.line));
831
+ }
832
+
833
+ function updateVerticalScroll(scrollTop) {
834
+ var th = textHeight(), virtualHeight = Math.floor(doc.height * th + 2 * paddingTop()), scrollbarHeight = scroller.clientHeight;
835
+ scrollbar.style.height = scrollbarHeight + "px";
836
+ if (scroller.clientHeight)
837
+ scrollbarInner.style.height = virtualHeight + "px";
838
+ // Position the mover div to align with the current virtual scroll position
839
+ if (scrollTop != null) scrollbar.scrollTop = scrollTop;
840
+ mover.style.top = (displayOffset * th - scrollbar.scrollTop) + "px";
841
+ scrollbar.style.display = (virtualHeight > scrollbarHeight) ? "block" : "none";
842
+ }
843
+
844
+ // On Mac OS X Lion and up, detect whether the mouse is plugged in by measuring
845
+ // the width of a div with a scrollbar in it. If the width is <= 1, then
846
+ // the mouse isn't plugged in and scrollbars should overlap the content.
847
+ function overlapScrollbars() {
848
+ var tmpSb = document.createElement('div'),
849
+ tmpSbInner = document.createElement('div');
850
+ tmpSb.className = "CodeMirror-scrollbar";
851
+ tmpSb.style.cssText = "position: absolute; left: -9999px; height: 100px;";
852
+ tmpSbInner.className = "CodeMirror-scrollbar-inner";
853
+ tmpSbInner.style.height = "200px";
854
+ tmpSb.appendChild(tmpSbInner);
855
+
856
+ document.body.appendChild(tmpSb);
857
+ var result = (tmpSb.offsetWidth <= 1);
858
+ document.body.removeChild(tmpSb);
859
+ return result;
860
+ }
652
861
 
653
- // Make sure the scroll-size div has the correct height.
654
- code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
862
+ function computeMaxLength() {
863
+ var maxLineLength = 0;
864
+ maxLine = ""; maxLineChanged = true;
865
+ doc.iter(0, doc.size, function(line) {
866
+ var l = line.text;
867
+ if (!line.hidden && l.length > maxLineLength) {
868
+ maxLineLength = l.length; maxLine = l;
869
+ }
870
+ });
871
+ updateMaxLine = false;
655
872
  }
656
873
 
657
874
  function replaceRange(code, from, to) {
@@ -729,7 +946,7 @@ var CodeMirror = (function() {
729
946
  // supported or compatible enough yet to rely on.)
730
947
  var prevInput = "";
731
948
  function readInput() {
732
- if (leaveInputAlone || !focused || hasSelection(input)) return false;
949
+ if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
733
950
  var text = input.value;
734
951
  if (text == prevInput) return false;
735
952
  shiftSelecting = null;
@@ -740,19 +957,20 @@ var CodeMirror = (function() {
740
957
  else if (overwrite && posEq(sel.from, sel.to))
741
958
  sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
742
959
  replaceSelection(text.slice(same), "end");
743
- prevInput = text;
960
+ if (text.length > 1000) { input.value = prevInput = ""; }
961
+ else prevInput = text;
744
962
  return true;
745
963
  }
746
964
  function resetInput(user) {
747
965
  if (!posEq(sel.from, sel.to)) {
748
966
  prevInput = "";
749
967
  input.value = getSelection();
750
- input.select();
968
+ selectInput(input);
751
969
  } else if (user) prevInput = input.value = "";
752
970
  }
753
971
 
754
972
  function focusInput() {
755
- if (!options.readOnly) input.focus();
973
+ if (options.readOnly != "nocursor") input.focus();
756
974
  }
757
975
 
758
976
  function scrollEditorIntoView() {
@@ -761,55 +979,67 @@ var CodeMirror = (function() {
761
979
  // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
762
980
  if (ie && rect.top == rect.bottom) return;
763
981
  var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
764
- if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
982
+ if (rect.top < 0 || rect.bottom > winH) scrollCursorIntoView();
765
983
  }
766
984
  function scrollCursorIntoView() {
985
+ var coords = calculateCursorCoords();
986
+ return scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
987
+ }
988
+ function calculateCursorCoords() {
767
989
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
768
990
  var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
769
- return scrollIntoView(x, cursor.y, x, cursor.yBot);
991
+ return {x: x, y: cursor.y, yBot: cursor.yBot};
770
992
  }
771
993
  function scrollIntoView(x1, y1, x2, y2) {
772
- var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
994
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2), scrolled = false;
995
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft; scrolled = true;}
996
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scrollPos.scrollTop; scrolled = true;}
997
+ if (scrolled && options.onScroll) options.onScroll(instance);
998
+ }
999
+ function calculateScrollPos(x1, y1, x2, y2) {
1000
+ var pl = paddingLeft(), pt = paddingTop();
773
1001
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
774
- var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
775
- if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
776
- else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
1002
+ var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
1003
+ var atTop = y1 < paddingTop() + 10;
1004
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1005
+ else if (y2 > screentop + screen) result.scrollTop = y2 - screen;
777
1006
 
778
1007
  var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
779
1008
  var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
780
- if (x1 < screenleft + gutterw) {
781
- if (x1 < 50) x1 = 0;
782
- scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
783
- scrolled = true;
1009
+ var atLeft = x1 < gutterw + pl + 10;
1010
+ if (x1 < screenleft + gutterw || atLeft) {
1011
+ if (atLeft) x1 = 0;
1012
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
1013
+ } else if (x2 > screenw + screenleft - 3) {
1014
+ result.scrollLeft = x2 + 10 - screenw;
784
1015
  }
785
- else if (x2 > screenw + screenleft - 3) {
786
- scroller.scrollLeft = x2 + 10 - screenw;
787
- scrolled = true;
788
- if (x2 > code.clientWidth) result = false;
789
- }
790
- if (scrolled && options.onScroll) options.onScroll(instance);
791
1016
  return result;
792
1017
  }
793
1018
 
794
- function visibleLines() {
795
- var lh = textHeight(), top = scroller.scrollTop - paddingTop();
796
- var from_height = Math.max(0, Math.floor(top / lh));
797
- var to_height = Math.ceil((top + scroller.clientHeight) / lh);
798
- return {from: lineAtHeight(doc, from_height),
799
- to: lineAtHeight(doc, to_height)};
1019
+ function visibleLines(scrollTop) {
1020
+ var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop();
1021
+ var fromHeight = Math.max(0, Math.floor(top / lh));
1022
+ var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
1023
+ return {from: lineAtHeight(doc, fromHeight),
1024
+ to: lineAtHeight(doc, toHeight)};
800
1025
  }
801
1026
  // Uses a set of changes plus the current scroll position to
802
1027
  // determine which DOM updates have to be made, and makes the
803
1028
  // updates.
804
- function updateDisplay(changes, suppressCallback) {
1029
+ function updateDisplay(changes, suppressCallback, scrollTop) {
805
1030
  if (!scroller.clientWidth) {
806
1031
  showingFrom = showingTo = displayOffset = 0;
807
1032
  return;
808
1033
  }
809
1034
  // Compute the new visible window
810
- var visible = visibleLines();
1035
+ // If scrollTop is specified, use that to determine which lines
1036
+ // to render instead of the current scrollbar position.
1037
+ var visible = visibleLines(scrollTop);
811
1038
  // Bail out if the visible area is already rendered and nothing changed.
812
- if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
1039
+ if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) {
1040
+ updateVerticalScroll(scrollTop);
1041
+ return;
1042
+ }
813
1043
  var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
814
1044
  if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
815
1045
  if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
@@ -827,24 +1057,23 @@ var CodeMirror = (function() {
827
1057
  if (range.from >= range.to) intact.splice(i--, 1);
828
1058
  else intactLines += range.to - range.from;
829
1059
  }
830
- if (intactLines == to - from) return;
1060
+ if (intactLines == to - from && from == showingFrom && to == showingTo) {
1061
+ updateVerticalScroll(scrollTop);
1062
+ return;
1063
+ }
831
1064
  intact.sort(function(a, b) {return a.domStart - b.domStart;});
832
1065
 
833
1066
  var th = textHeight(), gutterDisplay = gutter.style.display;
834
- lineDiv.style.display = gutter.style.display = "none";
1067
+ lineDiv.style.display = "none";
835
1068
  patchDisplay(from, to, intact);
836
- lineDiv.style.display = "";
1069
+ lineDiv.style.display = gutter.style.display = "";
837
1070
 
838
- // Position the mover div to align with the lines it's supposed
839
- // to be showing (which will cover the visible display)
840
1071
  var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
841
1072
  // This is just a bogus formula that detects when the editor is
842
1073
  // resized or the font size changes.
843
1074
  if (different) lastSizeC = scroller.clientHeight + th;
844
1075
  showingFrom = from; showingTo = to;
845
1076
  displayOffset = heightAtLine(doc, from);
846
- mover.style.top = (displayOffset * th) + "px";
847
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
848
1077
 
849
1078
  // Since this is all rather error prone, it is honoured with the
850
1079
  // only assertion in the whole file.
@@ -852,30 +1081,38 @@ var CodeMirror = (function() {
852
1081
  throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
853
1082
  " nodes=" + lineDiv.childNodes.length);
854
1083
 
855
- if (options.lineWrapping) {
856
- maxWidth = scroller.clientWidth;
857
- var curNode = lineDiv.firstChild;
1084
+ function checkHeights() {
1085
+ var curNode = lineDiv.firstChild, heightChanged = false;
858
1086
  doc.iter(showingFrom, showingTo, function(line) {
859
1087
  if (!line.hidden) {
860
1088
  var height = Math.round(curNode.offsetHeight / th) || 1;
861
- if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
1089
+ if (line.height != height) {
1090
+ updateLineHeight(line, height);
1091
+ gutterDirty = heightChanged = true;
1092
+ }
862
1093
  }
863
1094
  curNode = curNode.nextSibling;
864
1095
  });
865
- } else {
866
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
867
- if (maxWidth > scroller.clientWidth) {
868
- lineSpace.style.width = maxWidth + "px";
869
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
870
- code.style.width = "";
871
- code.style.width = scroller.scrollWidth + "px";
872
- } else {
873
- lineSpace.style.width = code.style.width = "";
874
- }
1096
+ return heightChanged;
875
1097
  }
1098
+
1099
+ if (options.lineWrapping) {
1100
+ // Guess whether we're going to need the scrollbar, so that we don't end up changing the linewrapping
1101
+ // after the scrollbar appears (during updateVerticalScroll()). Only do this if the scrollbar is
1102
+ // appearing (if it's disappearing, we don't have to worry about the scroll position, and there are
1103
+ // issues on IE7 if we turn it off too early).
1104
+ var virtualHeight = Math.floor(doc.height * th + 2 * paddingTop()), scrollbarHeight = scroller.clientHeight;
1105
+ if (virtualHeight > scrollbarHeight) scrollbar.style.display = "block";
1106
+ checkHeights();
1107
+ }
1108
+
876
1109
  gutter.style.display = gutterDisplay;
877
- if (different || gutterDirty) updateGutter();
878
- updateCursor();
1110
+ if (different || gutterDirty) {
1111
+ // If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
1112
+ updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
1113
+ }
1114
+ updateSelection();
1115
+ updateVerticalScroll(scrollTop);
879
1116
  if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
880
1117
  return true;
881
1118
  }
@@ -922,21 +1159,20 @@ var CodeMirror = (function() {
922
1159
  }
923
1160
  // This pass fills in the lines that actually changed.
924
1161
  var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
925
- var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
926
- var scratch = targetDocument.createElement("div"), newElt;
1162
+ var scratch = document.createElement("div");
927
1163
  doc.iter(from, to, function(line) {
928
- var ch1 = null, ch2 = null;
929
- if (inSel) {
930
- ch1 = 0;
931
- if (sto == j) {inSel = false; ch2 = sel.to.ch;}
932
- } else if (sfrom == j) {
933
- if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
934
- else {inSel = true; ch1 = sel.from.ch;}
935
- }
936
1164
  if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
937
1165
  if (!nextIntact || nextIntact.from > j) {
938
- if (line.hidden) scratch.innerHTML = "<pre></pre>";
939
- else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
1166
+ if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
1167
+ else {
1168
+ var html = '<pre' + (line.className ? ' class="' + line.className + '"' : '') + '>'
1169
+ + line.getHTML(makeTab) + '</pre>';
1170
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
1171
+ if (line.bgClassName)
1172
+ html = '<div style="position: relative"><pre class="' + line.bgClassName +
1173
+ '" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>' + html + "</div>";
1174
+ }
1175
+ scratch.innerHTML = html;
940
1176
  lineDiv.insertBefore(scratch.firstChild, curNode);
941
1177
  } else {
942
1178
  curNode = curNode.nextSibling;
@@ -949,7 +1185,7 @@ var CodeMirror = (function() {
949
1185
  if (!options.gutter && !options.lineNumbers) return;
950
1186
  var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
951
1187
  gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
952
- var html = [], i = showingFrom;
1188
+ var html = [], i = showingFrom, normalNode;
953
1189
  doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
954
1190
  if (line.hidden) {
955
1191
  html.push("<pre></pre>");
@@ -963,30 +1199,62 @@ var CodeMirror = (function() {
963
1199
  html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
964
1200
  for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
965
1201
  html.push("</pre>");
1202
+ if (!marker) normalNode = i;
966
1203
  }
967
1204
  ++i;
968
1205
  });
969
1206
  gutter.style.display = "none";
970
1207
  gutterText.innerHTML = html.join("");
971
- var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
972
- while (val.length + pad.length < minwidth) pad += "\u00a0";
973
- if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
1208
+ // Make sure scrolling doesn't cause number gutter size to pop
1209
+ if (normalNode != null && options.lineNumbers) {
1210
+ var node = gutterText.childNodes[normalNode - showingFrom];
1211
+ var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = "";
1212
+ while (val.length + pad.length < minwidth) pad += "\u00a0";
1213
+ if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
1214
+ }
974
1215
  gutter.style.display = "";
1216
+ var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2;
975
1217
  lineSpace.style.marginLeft = gutter.offsetWidth + "px";
976
1218
  gutterDirty = false;
1219
+ return resized;
977
1220
  }
978
- function updateCursor() {
979
- var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
980
- var pos = localCoords(head, true);
1221
+ function updateSelection() {
1222
+ var collapsed = posEq(sel.from, sel.to);
1223
+ var fromPos = localCoords(sel.from, true);
1224
+ var toPos = collapsed ? fromPos : localCoords(sel.to, true);
1225
+ var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
981
1226
  var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
982
- inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
983
- inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
984
- if (posEq(sel.from, sel.to)) {
985
- cursor.style.top = pos.y + "px";
986
- cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
1227
+ inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
1228
+ inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
1229
+ if (collapsed) {
1230
+ cursor.style.top = headPos.y + "px";
1231
+ cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
987
1232
  cursor.style.display = "";
1233
+ selectionDiv.style.display = "none";
1234
+ } else {
1235
+ var sameLine = fromPos.y == toPos.y, html = "";
1236
+ var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1237
+ var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1238
+ function add(left, top, right, height) {
1239
+ var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
1240
+ : "right: " + right + "px";
1241
+ html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
1242
+ 'px; top: ' + top + 'px; ' + rstyle + '; height: ' + height + 'px"></div>';
1243
+ }
1244
+ if (sel.from.ch && fromPos.y >= 0) {
1245
+ var right = sameLine ? clientWidth - toPos.x : 0;
1246
+ add(fromPos.x, fromPos.y, right, th);
1247
+ }
1248
+ var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
1249
+ var middleHeight = Math.min(toPos.y, clientHeight) - middleStart;
1250
+ if (middleHeight > 0.2 * th)
1251
+ add(0, middleStart, 0, middleHeight);
1252
+ if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
1253
+ add(0, toPos.y, clientWidth - toPos.x, th);
1254
+ selectionDiv.innerHTML = html;
1255
+ cursor.style.display = "none";
1256
+ selectionDiv.style.display = "";
988
1257
  }
989
- else cursor.style.display = "none";
990
1258
  }
991
1259
 
992
1260
  function setShift(val) {
@@ -1012,37 +1280,32 @@ var CodeMirror = (function() {
1012
1280
  if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1013
1281
 
1014
1282
  // Skip over hidden lines.
1015
- if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
1283
+ if (from.line != oldFrom) {
1284
+ var from1 = skipHidden(from, oldFrom, sel.from.ch);
1285
+ // If there is no non-hidden line left, force visibility on current line
1286
+ if (!from1) setLineHidden(from.line, false);
1287
+ else from = from1;
1288
+ }
1016
1289
  if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1017
1290
 
1018
1291
  if (posEq(from, to)) sel.inverted = false;
1019
1292
  else if (posEq(from, sel.to)) sel.inverted = false;
1020
1293
  else if (posEq(to, sel.from)) sel.inverted = true;
1021
1294
 
1022
- // Some ugly logic used to only mark the lines that actually did
1023
- // see a change in selection as changed, rather than the whole
1024
- // selected range.
1025
- if (posEq(from, to)) {
1026
- if (!posEq(sel.from, sel.to))
1027
- changes.push({from: oldFrom, to: oldTo + 1});
1028
- }
1029
- else if (posEq(sel.from, sel.to)) {
1030
- changes.push({from: from.line, to: to.line + 1});
1031
- }
1032
- else {
1033
- if (!posEq(from, sel.from)) {
1034
- if (from.line < oldFrom)
1035
- changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
1036
- else
1037
- changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
1038
- }
1039
- if (!posEq(to, sel.to)) {
1040
- if (to.line < oldTo)
1041
- changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
1042
- else
1043
- changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
1295
+ if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
1296
+ var head = sel.inverted ? from : to;
1297
+ if (head.line != sel.from.line && sel.from.line < doc.size) {
1298
+ var oldLine = getLine(sel.from.line);
1299
+ if (/^\s+$/.test(oldLine.text))
1300
+ setTimeout(operation(function() {
1301
+ if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
1302
+ var no = lineNo(oldLine);
1303
+ replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
1304
+ }
1305
+ }, 10));
1044
1306
  }
1045
1307
  }
1308
+
1046
1309
  sel.from = from; sel.to = to;
1047
1310
  selectionChanged = true;
1048
1311
  }
@@ -1053,13 +1316,14 @@ var CodeMirror = (function() {
1053
1316
  var line = getLine(lNo);
1054
1317
  if (!line.hidden) {
1055
1318
  var ch = pos.ch;
1056
- if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1319
+ if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
1057
1320
  return {line: lNo, ch: ch};
1058
1321
  }
1059
1322
  lNo += dir;
1060
1323
  }
1061
1324
  }
1062
1325
  var line = getLine(pos.line);
1326
+ var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
1063
1327
  if (!line.hidden) return pos;
1064
1328
  if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1065
1329
  else return getNonHidden(-1) || getNonHidden(1);
@@ -1123,9 +1387,10 @@ var CodeMirror = (function() {
1123
1387
  function moveV(dir, unit) {
1124
1388
  var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1125
1389
  if (goalColumn != null) pos.x = goalColumn;
1126
- if (unit == "page") dist = scroller.clientHeight;
1390
+ if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1127
1391
  else if (unit == "line") dist = textHeight();
1128
1392
  var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1393
+ if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
1129
1394
  setCursor(target.line, target.ch, true);
1130
1395
  goalColumn = pos.x;
1131
1396
  }
@@ -1138,7 +1403,7 @@ var CodeMirror = (function() {
1138
1403
  setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1139
1404
  }
1140
1405
  function selectLine(line) {
1141
- setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
1406
+ setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
1142
1407
  }
1143
1408
  function indentSelected(mode) {
1144
1409
  if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
@@ -1155,11 +1420,14 @@ var CodeMirror = (function() {
1155
1420
 
1156
1421
  var line = getLine(n), curSpace = line.indentation(options.tabSize),
1157
1422
  curSpaceString = line.text.match(/^\s*/)[0], indentation;
1423
+ if (how == "smart") {
1424
+ indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1425
+ if (indentation == Pass) how = "prev";
1426
+ }
1158
1427
  if (how == "prev") {
1159
1428
  if (n) indentation = getLine(n-1).indentation(options.tabSize);
1160
1429
  else indentation = 0;
1161
1430
  }
1162
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1163
1431
  else if (how == "add") indentation = curSpace + options.indentUnit;
1164
1432
  else if (how == "subtract") indentation = curSpace - options.indentUnit;
1165
1433
  indentation = Math.max(0, indentation);
@@ -1168,8 +1436,7 @@ var CodeMirror = (function() {
1168
1436
  if (!diff) {
1169
1437
  if (sel.from.line != n && sel.to.line != n) return;
1170
1438
  var indentString = curSpaceString;
1171
- }
1172
- else {
1439
+ } else {
1173
1440
  var indentString = "", pos = 0;
1174
1441
  if (options.indentWithTabs)
1175
1442
  for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
@@ -1201,9 +1468,10 @@ var CodeMirror = (function() {
1201
1468
  if (guess != 1) updateLineHeight(line, guess);
1202
1469
  });
1203
1470
  lineSpace.style.width = code.style.width = "";
1471
+ widthForcer.style.left = "";
1204
1472
  } else {
1205
1473
  wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1206
- maxWidth = null; maxLine = "";
1474
+ maxLine = ""; maxLineChanged = true;
1207
1475
  doc.iter(0, doc.size, function(line) {
1208
1476
  if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1209
1477
  if (line.text.length > maxLine.length) maxLine = line.text;
@@ -1211,18 +1479,21 @@ var CodeMirror = (function() {
1211
1479
  }
1212
1480
  changes.push({from: 0, to: doc.size});
1213
1481
  }
1214
- function computeTabText() {
1215
- for (var str = '<span class="cm-tab">', i = 0; i < options.tabSize; ++i) str += " ";
1216
- return str + "</span>";
1217
- }
1218
- function tabsChanged() {
1219
- tabText = computeTabText();
1220
- updateDisplay(true);
1482
+ function makeTab(col) {
1483
+ var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
1484
+ if (cached) return cached;
1485
+ for (var str = '<span class="cm-tab">', i = 0; i < w; ++i) str += " ";
1486
+ return (tabCache[w] = {html: str + "</span>", width: w});
1221
1487
  }
1222
1488
  function themeChanged() {
1223
- scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
1489
+ scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1224
1490
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1225
1491
  }
1492
+ function keyMapChanged() {
1493
+ var style = keyMap[options.keyMap].style;
1494
+ wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
1495
+ (style ? " cm-keymap-" + style : "");
1496
+ }
1226
1497
 
1227
1498
  function TextMarker() { this.set = []; }
1228
1499
  TextMarker.prototype.clear = operation(function() {
@@ -1233,7 +1504,7 @@ var CodeMirror = (function() {
1233
1504
  var lineN = lineNo(line);
1234
1505
  min = Math.min(min, lineN); max = Math.max(max, lineN);
1235
1506
  for (var j = 0; j < mk.length; ++j)
1236
- if (mk[j].set == this.set) mk.splice(j--, 1);
1507
+ if (mk[j].marker == this) mk.splice(j--, 1);
1237
1508
  }
1238
1509
  if (min != Infinity)
1239
1510
  changes.push({from: min, to: max + 1});
@@ -1244,7 +1515,7 @@ var CodeMirror = (function() {
1244
1515
  var line = this.set[i], mk = line.marked;
1245
1516
  for (var j = 0; j < mk.length; ++j) {
1246
1517
  var mark = mk[j];
1247
- if (mark.set == this.set) {
1518
+ if (mark.marker == this) {
1248
1519
  if (mark.from != null || mark.to != null) {
1249
1520
  var found = lineNo(line);
1250
1521
  if (found != null) {
@@ -1261,8 +1532,9 @@ var CodeMirror = (function() {
1261
1532
  function markText(from, to, className) {
1262
1533
  from = clipPos(from); to = clipPos(to);
1263
1534
  var tm = new TextMarker();
1535
+ if (!posLess(from, to)) return tm;
1264
1536
  function add(line, from, to, className) {
1265
- getLine(line).addMark(new MarkedText(from, to, className, tm.set));
1537
+ getLine(line).addMark(new MarkedText(from, to, className, tm));
1266
1538
  }
1267
1539
  if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1268
1540
  else {
@@ -1282,6 +1554,19 @@ var CodeMirror = (function() {
1282
1554
  return bm;
1283
1555
  }
1284
1556
 
1557
+ function findMarksAt(pos) {
1558
+ pos = clipPos(pos);
1559
+ var markers = [], marked = getLine(pos.line).marked;
1560
+ if (!marked) return markers;
1561
+ for (var i = 0, e = marked.length; i < e; ++i) {
1562
+ var m = marked[i];
1563
+ if ((m.from == null || m.from <= pos.ch) &&
1564
+ (m.to == null || m.to >= pos.ch))
1565
+ markers.push(m.marker || m);
1566
+ }
1567
+ return markers;
1568
+ }
1569
+
1285
1570
  function addGutterMarker(line, text, className) {
1286
1571
  if (typeof line == "number") line = getLine(clipLine(line));
1287
1572
  line.gutterMarker = {text: text, style: className};
@@ -1303,10 +1588,11 @@ var CodeMirror = (function() {
1303
1588
  else return null;
1304
1589
  return line;
1305
1590
  }
1306
- function setLineClass(handle, className) {
1591
+ function setLineClass(handle, className, bgClassName) {
1307
1592
  return changeLine(handle, function(line) {
1308
- if (line.className != className) {
1593
+ if (line.className != className || line.bgClassName != bgClassName) {
1309
1594
  line.className = className;
1595
+ line.bgClassName = bgClassName;
1310
1596
  return true;
1311
1597
  }
1312
1598
  });
@@ -1315,10 +1601,23 @@ var CodeMirror = (function() {
1315
1601
  return changeLine(handle, function(line, no) {
1316
1602
  if (line.hidden != hidden) {
1317
1603
  line.hidden = hidden;
1604
+ if (!options.lineWrapping) {
1605
+ var l = line.text;
1606
+ if (hidden && l.length == maxLine.length) {
1607
+ updateMaxLine = true;
1608
+ } else if (!hidden && l.length > maxLine.length) {
1609
+ maxLine = l; maxWidth = null; updateMaxLine = false;
1610
+ }
1611
+ }
1318
1612
  updateLineHeight(line, hidden ? 0 : 1);
1319
- if (hidden && (sel.from.line == no || sel.to.line == no))
1320
- setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
1321
- skipHidden(sel.to, sel.to.line, sel.to.ch));
1613
+ var fline = sel.from.line, tline = sel.to.line;
1614
+ if (hidden && (fline == no || tline == no)) {
1615
+ var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
1616
+ var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
1617
+ // Can't hide the last visible line, we'd have no place to put the cursor
1618
+ if (!to) return;
1619
+ setSelection(from, to);
1620
+ }
1322
1621
  return (gutterDirty = true);
1323
1622
  }
1324
1623
  });
@@ -1330,14 +1629,13 @@ var CodeMirror = (function() {
1330
1629
  var n = line;
1331
1630
  line = getLine(line);
1332
1631
  if (!line) return null;
1333
- }
1334
- else {
1632
+ } else {
1335
1633
  var n = lineNo(line);
1336
1634
  if (n == null) return null;
1337
1635
  }
1338
1636
  var marker = line.gutterMarker;
1339
1637
  return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1340
- markerClass: marker && marker.style, lineClass: line.className};
1638
+ markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1341
1639
  }
1342
1640
 
1343
1641
  function stringWidth(str) {
@@ -1351,8 +1649,7 @@ var CodeMirror = (function() {
1351
1649
  if (x <= 0) return 0;
1352
1650
  var lineObj = getLine(line), text = lineObj.text;
1353
1651
  function getX(len) {
1354
- measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, tabText, len) + "</span></pre>";
1355
- return measure.firstChild.firstChild.offsetWidth;
1652
+ return measureLine(lineObj, len).left;
1356
1653
  }
1357
1654
  var from = 0, fromX = 0, to = text.length, toX;
1358
1655
  // Guess a suitable upper bound for our search.
@@ -1375,21 +1672,16 @@ var CodeMirror = (function() {
1375
1672
  }
1376
1673
  }
1377
1674
 
1378
- var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1675
+ var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16);
1379
1676
  function measureLine(line, ch) {
1380
- var extra = "";
1381
- // Include extra text at the end to make sure the measured line is wrapped in the right way.
1382
- if (options.lineWrapping) {
1383
- var end = line.text.indexOf(" ", ch + 2);
1384
- extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1385
- }
1386
- measure.innerHTML = "<pre>" + line.getHTML(null, null, false, tabText, ch) +
1387
- '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1388
- extra + "</pre>";
1389
- var elt = document.getElementById("CodeMirror-temp-" + tempId);
1677
+ if (ch == 0) return {top: 0, left: 0};
1678
+ var wbr = options.lineWrapping && ch < line.text.length &&
1679
+ spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
1680
+ measure.innerHTML = "<pre>" + line.getHTML(makeTab, ch, tempId, wbr) + "</pre>";
1681
+ var elt = document.getElementById(tempId);
1390
1682
  var top = elt.offsetTop, left = elt.offsetLeft;
1391
1683
  // Older IEs report zero offsets for spans directly after a wrap
1392
- if (ie && ch && top == 0 && left == 0) {
1684
+ if (ie && top == 0 && left == 0) {
1393
1685
  var backup = document.createElement("span");
1394
1686
  backup.innerHTML = "x";
1395
1687
  elt.parentNode.insertBefore(backup, elt.nextSibling);
@@ -1486,8 +1778,8 @@ var CodeMirror = (function() {
1486
1778
  return coordsChar(x - offL.left, y - offL.top);
1487
1779
  }
1488
1780
  function onContextMenu(e) {
1489
- var pos = posFromMouse(e);
1490
- if (!pos || window.opera) return; // Opera is difficult.
1781
+ var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
1782
+ if (!pos || opera) return; // Opera is difficult.
1491
1783
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1492
1784
  operation(setCursor)(pos.line, pos.ch);
1493
1785
 
@@ -1499,12 +1791,13 @@ var CodeMirror = (function() {
1499
1791
  leaveInputAlone = true;
1500
1792
  var val = input.value = getSelection();
1501
1793
  focusInput();
1502
- input.select();
1794
+ selectInput(input);
1503
1795
  function rehide() {
1504
1796
  var newVal = splitLines(input.value).join("\n");
1505
- if (newVal != val) operation(replaceSelection)(newVal, "end");
1797
+ if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
1506
1798
  inputDiv.style.position = "relative";
1507
1799
  input.style.cssText = oldCSS;
1800
+ if (ie_lt9) scrollbar.scrollTop = scrollPos;
1508
1801
  leaveInputAlone = false;
1509
1802
  resetInput(true);
1510
1803
  slowPoll();
@@ -1516,8 +1809,7 @@ var CodeMirror = (function() {
1516
1809
  mouseup();
1517
1810
  setTimeout(rehide, 20);
1518
1811
  }, true);
1519
- }
1520
- else {
1812
+ } else {
1521
1813
  setTimeout(rehide, 50);
1522
1814
  }
1523
1815
  }
@@ -1547,7 +1839,7 @@ var CodeMirror = (function() {
1547
1839
  var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1548
1840
  for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1549
1841
  var text = st[i];
1550
- if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1842
+ if (st[i+1] != style) {pos += d * text.length; continue;}
1551
1843
  for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1552
1844
  if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1553
1845
  var match = matching[cur];
@@ -1634,13 +1926,17 @@ var CodeMirror = (function() {
1634
1926
  var changed = line.highlight(mode, state, options.tabSize);
1635
1927
  if (changed) realChange = true;
1636
1928
  line.stateAfter = copyState(mode, state);
1929
+ var done = null;
1637
1930
  if (compare) {
1638
- if (hadState && compare(hadState, state)) return true;
1639
- } else {
1931
+ var same = hadState && compare(hadState, state);
1932
+ if (same != Pass) done = !!same;
1933
+ }
1934
+ if (done == null) {
1640
1935
  if (changed !== false || !hadState) unchanged = 0;
1641
1936
  else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1642
- return true;
1937
+ done = true;
1643
1938
  }
1939
+ if (done) return true;
1644
1940
  ++i;
1645
1941
  });
1646
1942
  if (bail) return;
@@ -1663,14 +1959,22 @@ var CodeMirror = (function() {
1663
1959
  changes = []; selectionChanged = false; callbacks = [];
1664
1960
  }
1665
1961
  function endOperation() {
1666
- var reScroll = false, updated;
1667
- if (selectionChanged) reScroll = !scrollCursorIntoView();
1668
- if (changes.length) updated = updateDisplay(changes, true);
1962
+ if (updateMaxLine) computeMaxLength();
1963
+ if (maxLineChanged && !options.lineWrapping) {
1964
+ widthForcer.style.left = stringWidth(maxLine) + "px";
1965
+ maxLineChanged = false;
1966
+ }
1967
+ var newScrollPos, updated;
1968
+ if (selectionChanged) {
1969
+ var coords = calculateCursorCoords();
1970
+ newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
1971
+ }
1972
+ if (changes.length) updated = updateDisplay(changes, true, (newScrollPos ? newScrollPos.scrollTop : null));
1669
1973
  else {
1670
- if (selectionChanged) updateCursor();
1974
+ if (selectionChanged) updateSelection();
1671
1975
  if (gutterDirty) updateGutter();
1672
1976
  }
1673
- if (reScroll) scrollCursorIntoView();
1977
+ if (newScrollPos) scrollCursorIntoView();
1674
1978
  if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1675
1979
 
1676
1980
  if (focused && !leaveInputAlone &&
@@ -1682,11 +1986,11 @@ var CodeMirror = (function() {
1682
1986
  if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1683
1987
  if (posEq(sel.from, sel.to)) matchBrackets(false);
1684
1988
  }), 20);
1685
- var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
1686
- if (selectionChanged && options.onCursorActivity)
1989
+ var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks
1990
+ if (textChanged && options.onChange && instance)
1991
+ options.onChange(instance, textChanged);
1992
+ if (sc && options.onCursorActivity)
1687
1993
  options.onCursorActivity(instance);
1688
- if (tc && options.onChange && instance)
1689
- options.onChange(instance, tc);
1690
1994
  for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1691
1995
  if (updated && options.onUpdate) options.onUpdate(instance);
1692
1996
  }
@@ -1700,6 +2004,11 @@ var CodeMirror = (function() {
1700
2004
  };
1701
2005
  }
1702
2006
 
2007
+ function compoundChange(f) {
2008
+ history.startCompound();
2009
+ try { return f(); } finally { history.endCompound(); }
2010
+ }
2011
+
1703
2012
  for (var ext in extensions)
1704
2013
  if (extensions.propertyIsEnumerable(ext) &&
1705
2014
  !instance.propertyIsEnumerable(ext))
@@ -1714,17 +2023,21 @@ var CodeMirror = (function() {
1714
2023
  theme: "default",
1715
2024
  indentUnit: 2,
1716
2025
  indentWithTabs: false,
2026
+ smartIndent: true,
1717
2027
  tabSize: 4,
1718
2028
  keyMap: "default",
1719
2029
  extraKeys: null,
1720
2030
  electricChars: true,
2031
+ autoClearEmptyLines: false,
1721
2032
  onKeyEvent: null,
2033
+ onDragEvent: null,
1722
2034
  lineWrapping: false,
1723
2035
  lineNumbers: false,
1724
2036
  gutter: false,
1725
2037
  fixedGutter: false,
1726
2038
  firstLineNumber: 1,
1727
2039
  readOnly: false,
2040
+ dragDrop: true,
1728
2041
  onChange: null,
1729
2042
  onCursorActivity: null,
1730
2043
  onGutterClick: null,
@@ -1737,34 +2050,39 @@ var CodeMirror = (function() {
1737
2050
  pollInterval: 100,
1738
2051
  undoDepth: 40,
1739
2052
  tabindex: null,
1740
- document: window.document
2053
+ autofocus: null
1741
2054
  };
1742
2055
 
1743
- var mac = /Mac/.test(navigator.platform);
2056
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
2057
+ var mac = ios || /Mac/.test(navigator.platform);
1744
2058
  var win = /Win/.test(navigator.platform);
1745
2059
 
1746
2060
  // Known modes, by name and by MIME
1747
- var modes = {}, mimeModes = {};
2061
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
1748
2062
  CodeMirror.defineMode = function(name, mode) {
1749
2063
  if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
2064
+ if (arguments.length > 2) {
2065
+ mode.dependencies = [];
2066
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
2067
+ }
1750
2068
  modes[name] = mode;
1751
2069
  };
1752
2070
  CodeMirror.defineMIME = function(mime, spec) {
1753
2071
  mimeModes[mime] = spec;
1754
2072
  };
1755
- CodeMirror.getMode = function(options, spec) {
2073
+ CodeMirror.resolveMode = function(spec) {
1756
2074
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1757
2075
  spec = mimeModes[spec];
1758
- if (typeof spec == "string")
1759
- var mname = spec, config = {};
1760
- else if (spec != null)
1761
- var mname = spec.name, config = spec;
1762
- var mfactory = modes[mname];
1763
- if (!mfactory) {
1764
- if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1765
- return CodeMirror.getMode(options, "text/plain");
1766
- }
1767
- return mfactory(options, config || {});
2076
+ else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
2077
+ return CodeMirror.resolveMode("application/xml");
2078
+ if (typeof spec == "string") return {name: spec};
2079
+ else return spec || {name: "null"};
2080
+ };
2081
+ CodeMirror.getMode = function(options, spec) {
2082
+ var spec = CodeMirror.resolveMode(spec);
2083
+ var mfactory = modes[spec.name];
2084
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2085
+ return mfactory(options, spec);
1768
2086
  };
1769
2087
  CodeMirror.listModes = function() {
1770
2088
  var list = [];
@@ -1821,6 +2139,10 @@ var CodeMirror = (function() {
1821
2139
  indentMore: function(cm) {cm.indentSelection("add");},
1822
2140
  indentLess: function(cm) {cm.indentSelection("subtract");},
1823
2141
  insertTab: function(cm) {cm.replaceSelection("\t", "end");},
2142
+ defaultTab: function(cm) {
2143
+ if (cm.somethingSelected()) cm.indentSelection("add");
2144
+ else cm.replaceSelection("\t", "end");
2145
+ },
1824
2146
  transposeChars: function(cm) {
1825
2147
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
1826
2148
  if (cur.ch > 0 && cur.ch < line.length - 1)
@@ -1838,7 +2160,7 @@ var CodeMirror = (function() {
1838
2160
  keyMap.basic = {
1839
2161
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
1840
2162
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
1841
- "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
2163
+ "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
1842
2164
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
1843
2165
  };
1844
2166
  // Note that the save and find-related commands aren't defined by
@@ -1849,6 +2171,7 @@ var CodeMirror = (function() {
1849
2171
  "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
1850
2172
  "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
1851
2173
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
2174
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
1852
2175
  fallthrough: "basic"
1853
2176
  };
1854
2177
  keyMap.macDefault = {
@@ -1857,6 +2180,7 @@ var CodeMirror = (function() {
1857
2180
  "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
1858
2181
  "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
1859
2182
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
2183
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore",
1860
2184
  fallthrough: ["basic", "emacsy"]
1861
2185
  };
1862
2186
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -1867,23 +2191,33 @@ var CodeMirror = (function() {
1867
2191
  "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
1868
2192
  };
1869
2193
 
1870
- function lookupKey(name, extraMap, map) {
1871
- function lookup(name, map, ft) {
2194
+ function getKeyMap(val) {
2195
+ if (typeof val == "string") return keyMap[val];
2196
+ else return val;
2197
+ }
2198
+ function lookupKey(name, extraMap, map, handle, stop) {
2199
+ function lookup(map) {
2200
+ map = getKeyMap(map);
1872
2201
  var found = map[name];
1873
- if (found != null) return found;
1874
- if (ft == null) ft = map.fallthrough;
1875
- if (ft == null) return map.catchall;
1876
- if (typeof ft == "string") return lookup(name, keyMap[ft]);
1877
- for (var i = 0, e = ft.length; i < e; ++i) {
1878
- found = lookup(name, keyMap[ft[i]]);
1879
- if (found != null) return found;
2202
+ if (found != null && handle(found)) return true;
2203
+ if (map.nofallthrough) {
2204
+ if (stop) stop();
2205
+ return true;
1880
2206
  }
1881
- return null;
2207
+ var fallthrough = map.fallthrough;
2208
+ if (fallthrough == null) return false;
2209
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
2210
+ return lookup(fallthrough);
2211
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
2212
+ if (lookup(fallthrough[i])) return true;
2213
+ }
2214
+ return false;
1882
2215
  }
1883
- return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
2216
+ if (extraMap && lookup(extraMap)) return true;
2217
+ return lookup(map);
1884
2218
  }
1885
2219
  function isModifierKey(event) {
1886
- var name = keyNames[event.keyCode];
2220
+ var name = keyNames[e_prop(event, "keyCode")];
1887
2221
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1888
2222
  }
1889
2223
 
@@ -1892,6 +2226,8 @@ var CodeMirror = (function() {
1892
2226
  options.value = textarea.value;
1893
2227
  if (!options.tabindex && textarea.tabindex)
1894
2228
  options.tabindex = textarea.tabindex;
2229
+ if (options.autofocus == null && textarea.getAttribute("autofocus") != null)
2230
+ options.autofocus = true;
1895
2231
 
1896
2232
  function save() {textarea.value = instance.getValue();}
1897
2233
  if (textarea.form) {
@@ -1992,8 +2328,7 @@ var CodeMirror = (function() {
1992
2328
  if (consume !== false) this.pos += pattern.length;
1993
2329
  return true;
1994
2330
  }
1995
- }
1996
- else {
2331
+ } else {
1997
2332
  var match = this.string.slice(this.pos).match(pattern);
1998
2333
  if (match && consume !== false) this.pos += match[0].length;
1999
2334
  return match;
@@ -2003,34 +2338,34 @@ var CodeMirror = (function() {
2003
2338
  };
2004
2339
  CodeMirror.StringStream = StringStream;
2005
2340
 
2006
- function MarkedText(from, to, className, set) {
2007
- this.from = from; this.to = to; this.style = className; this.set = set;
2341
+ function MarkedText(from, to, className, marker) {
2342
+ this.from = from; this.to = to; this.style = className; this.marker = marker;
2008
2343
  }
2009
2344
  MarkedText.prototype = {
2010
- attach: function(line) { this.set.push(line); },
2345
+ attach: function(line) { this.marker.set.push(line); },
2011
2346
  detach: function(line) {
2012
- var ix = indexOf(this.set, line);
2013
- if (ix > -1) this.set.splice(ix, 1);
2347
+ var ix = indexOf(this.marker.set, line);
2348
+ if (ix > -1) this.marker.set.splice(ix, 1);
2014
2349
  },
2015
2350
  split: function(pos, lenBefore) {
2016
2351
  if (this.to <= pos && this.to != null) return null;
2017
2352
  var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2018
2353
  var to = this.to == null ? null : this.to - pos + lenBefore;
2019
- return new MarkedText(from, to, this.style, this.set);
2354
+ return new MarkedText(from, to, this.style, this.marker);
2020
2355
  },
2021
- dup: function() { return new MarkedText(null, null, this.style, this.set); },
2356
+ dup: function() { return new MarkedText(null, null, this.style, this.marker); },
2022
2357
  clipTo: function(fromOpen, from, toOpen, to, diff) {
2023
- if (this.from != null && this.from >= from)
2024
- this.from = Math.max(to, this.from) + diff;
2025
- if (this.to != null && this.to > from)
2026
- this.to = to < this.to ? this.to + diff : from;
2027
2358
  if (fromOpen && to > this.from && (to < this.to || this.to == null))
2028
2359
  this.from = null;
2360
+ else if (this.from != null && this.from >= from)
2361
+ this.from = Math.max(to, this.from) + diff;
2029
2362
  if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2030
2363
  this.to = null;
2364
+ else if (this.to != null && this.to > from)
2365
+ this.to = to < this.to ? this.to + diff : from;
2031
2366
  },
2032
2367
  isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2033
- sameSet: function(x) { return this.set == x.set; }
2368
+ sameSet: function(x) { return this.marker == x.marker; }
2034
2369
  };
2035
2370
 
2036
2371
  function Bookmark(pos) {
@@ -2073,7 +2408,7 @@ var CodeMirror = (function() {
2073
2408
  this.styles = styles || [text, null];
2074
2409
  this.text = text;
2075
2410
  this.height = 1;
2076
- this.marked = this.gutterMarker = this.className = this.handlers = null;
2411
+ this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null;
2077
2412
  this.stateAfter = this.parent = this.hidden = null;
2078
2413
  }
2079
2414
  Line.inheritMarks = function(text, orig) {
@@ -2100,7 +2435,8 @@ var CodeMirror = (function() {
2100
2435
  this.stateAfter = null;
2101
2436
  if (mk) {
2102
2437
  var diff = text.length - (to - from);
2103
- for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
2438
+ for (var i = 0; i < mk.length; ++i) {
2439
+ var mark = mk[i];
2104
2440
  mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2105
2441
  if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2106
2442
  }
@@ -2118,6 +2454,7 @@ var CodeMirror = (function() {
2118
2454
  if (newmark) {
2119
2455
  if (!taken.marked) taken.marked = [];
2120
2456
  taken.marked.push(newmark); newmark.attach(taken);
2457
+ if (newmark == mark) mk.splice(i--, 1);
2121
2458
  }
2122
2459
  }
2123
2460
  }
@@ -2227,73 +2564,116 @@ var CodeMirror = (function() {
2227
2564
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2228
2565
  // Produces an HTML fragment for the line, taking selection,
2229
2566
  // marking, and highlighting into account.
2230
- getHTML: function(sfrom, sto, includePre, tabText, endAt) {
2231
- var html = [], first = true;
2232
- if (includePre)
2233
- html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
2234
- function span(text, style) {
2567
+ getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) {
2568
+ var html = [], first = true, col = 0;
2569
+ function span_(text, style) {
2235
2570
  if (!text) return;
2236
2571
  // Work around a bug where, in some compat modes, IE ignores leading spaces
2237
2572
  if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2238
2573
  first = false;
2239
- if (style) html.push('<span class="', style, '">', htmlEscape(text).replace(/\t/g, tabText), "</span>");
2240
- else html.push(htmlEscape(text).replace(/\t/g, tabText));
2574
+ if (text.indexOf("\t") == -1) {
2575
+ col += text.length;
2576
+ var escaped = htmlEscape(text);
2577
+ } else {
2578
+ var escaped = "";
2579
+ for (var pos = 0;;) {
2580
+ var idx = text.indexOf("\t", pos);
2581
+ if (idx == -1) {
2582
+ escaped += htmlEscape(text.slice(pos));
2583
+ col += text.length - pos;
2584
+ break;
2585
+ } else {
2586
+ col += idx - pos;
2587
+ var tab = makeTab(col);
2588
+ escaped += htmlEscape(text.slice(pos, idx)) + tab.html;
2589
+ col += tab.width;
2590
+ pos = idx + 1;
2591
+ }
2592
+ }
2593
+ }
2594
+ if (style) html.push('<span class="', style, '">', escaped, "</span>");
2595
+ else html.push(escaped);
2596
+ }
2597
+ var span = span_;
2598
+ if (wrapAt != null) {
2599
+ var outPos = 0, open = "<span id=\"" + wrapId + "\">";
2600
+ span = function(text, style) {
2601
+ var l = text.length;
2602
+ if (wrapAt >= outPos && wrapAt < outPos + l) {
2603
+ if (wrapAt > outPos) {
2604
+ span_(text.slice(0, wrapAt - outPos), style);
2605
+ // See comment at the definition of spanAffectsWrapping
2606
+ if (wrapWBR) html.push("<wbr>");
2607
+ }
2608
+ html.push(open);
2609
+ var cut = wrapAt - outPos;
2610
+ span_(opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2611
+ html.push("</span>");
2612
+ if (opera) span_(text.slice(cut + 1), style);
2613
+ wrapAt--;
2614
+ outPos += l;
2615
+ } else {
2616
+ outPos += l;
2617
+ span_(text, style);
2618
+ // Output empty wrapper when at end of line
2619
+ if (outPos == wrapAt && outPos == len) html.push(open + " </span>");
2620
+ // Stop outputting HTML when gone sufficiently far beyond measure
2621
+ else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2622
+ }
2623
+ }
2241
2624
  }
2625
+
2242
2626
  var st = this.styles, allText = this.text, marked = this.marked;
2243
- if (sfrom == sto) sfrom = null;
2244
2627
  var len = allText.length;
2245
- if (endAt != null) len = Math.min(endAt, len);
2628
+ function styleToClass(style) {
2629
+ if (!style) return null;
2630
+ return "cm-" + style.replace(/ +/g, " cm-");
2631
+ }
2246
2632
 
2247
- if (!allText && endAt == null)
2248
- span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
2249
- else if (!marked && sfrom == null)
2633
+ if (!allText && wrapAt == null) {
2634
+ span(" ");
2635
+ } else if (!marked || !marked.length) {
2250
2636
  for (var i = 0, ch = 0; ch < len; i+=2) {
2251
2637
  var str = st[i], style = st[i+1], l = str.length;
2252
2638
  if (ch + l > len) str = str.slice(0, len - ch);
2253
2639
  ch += l;
2254
- span(str, style && "cm-" + style);
2640
+ span(str, styleToClass(style));
2255
2641
  }
2256
- else {
2642
+ } else {
2257
2643
  var pos = 0, i = 0, text = "", style, sg = 0;
2258
- var markpos = -1, mark = null;
2259
- function nextMark() {
2260
- if (marked) {
2261
- markpos += 1;
2262
- mark = (markpos < marked.length) ? marked[markpos] : null;
2644
+ var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2645
+ function advanceMarks() {
2646
+ var m;
2647
+ while (markpos < marked.length &&
2648
+ ((m = marked[markpos]).from == pos || m.from == null)) {
2649
+ if (m.style != null) marks.push(m);
2650
+ ++markpos;
2651
+ }
2652
+ nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2653
+ for (var i = 0; i < marks.length; ++i) {
2654
+ var to = marks[i].to || Infinity;
2655
+ if (to == pos) marks.splice(i--, 1);
2656
+ else nextChange = Math.min(to, nextChange);
2263
2657
  }
2264
2658
  }
2265
- nextMark();
2659
+ var m = 0;
2266
2660
  while (pos < len) {
2267
- var upto = len;
2268
- var extraStyle = "";
2269
- if (sfrom != null) {
2270
- if (sfrom > pos) upto = sfrom;
2271
- else if (sto == null || sto > pos) {
2272
- extraStyle = " CodeMirror-selected";
2273
- if (sto != null) upto = Math.min(upto, sto);
2274
- }
2275
- }
2276
- while (mark && mark.to != null && mark.to <= pos) nextMark();
2277
- if (mark) {
2278
- if (mark.from > pos) upto = Math.min(upto, mark.from);
2279
- else {
2280
- extraStyle += " " + mark.style;
2281
- if (mark.to != null) upto = Math.min(upto, mark.to);
2661
+ if (nextChange == pos) advanceMarks();
2662
+ var upto = Math.min(len, nextChange);
2663
+ while (true) {
2664
+ if (text) {
2665
+ var end = pos + text.length;
2666
+ var appliedStyle = style;
2667
+ for (var j = 0; j < marks.length; ++j)
2668
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2669
+ span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2670
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2671
+ pos = end;
2282
2672
  }
2283
- }
2284
- for (;;) {
2285
- var end = pos + text.length;
2286
- var appliedStyle = style;
2287
- if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
2288
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2289
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2290
- pos = end;
2291
- text = st[i++]; style = "cm-" + st[i++];
2673
+ text = st[i++]; style = styleToClass(st[i++]);
2292
2674
  }
2293
2675
  }
2294
- if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
2295
2676
  }
2296
- if (includePre) html.push("</pre>");
2297
2677
  return html.join("");
2298
2678
  },
2299
2679
  cleanUp: function() {
@@ -2309,8 +2689,7 @@ var CodeMirror = (function() {
2309
2689
  if (state == 0) {
2310
2690
  if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2311
2691
  if (end >= from) state = 1;
2312
- }
2313
- else if (state == 1) {
2692
+ } else if (state == 1) {
2314
2693
  if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2315
2694
  else dest.push(part, source[i+1]);
2316
2695
  }
@@ -2345,7 +2724,7 @@ var CodeMirror = (function() {
2345
2724
  },
2346
2725
  insertHeight: function(at, lines, height) {
2347
2726
  this.height += height;
2348
- this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2727
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
2349
2728
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2350
2729
  },
2351
2730
  iterN: function(at, n, op) {
@@ -2384,6 +2763,7 @@ var CodeMirror = (function() {
2384
2763
  var lines = [];
2385
2764
  this.collapse(lines);
2386
2765
  this.children = [new LeafChunk(lines)];
2766
+ this.children[0].parent = this;
2387
2767
  }
2388
2768
  },
2389
2769
  collapse: function(lines) {
@@ -2510,31 +2890,36 @@ var CodeMirror = (function() {
2510
2890
  function History() {
2511
2891
  this.time = 0;
2512
2892
  this.done = []; this.undone = [];
2893
+ this.compound = 0;
2894
+ this.closed = false;
2513
2895
  }
2514
2896
  History.prototype = {
2515
2897
  addChange: function(start, added, old) {
2516
2898
  this.undone.length = 0;
2517
- var time = +new Date, last = this.done[this.done.length - 1];
2518
- if (time - this.time > 400 || !last ||
2519
- last.start > start + added || last.start + last.added < start - last.added + last.old.length)
2520
- this.done.push({start: start, added: added, old: old});
2521
- else {
2522
- var oldoff = 0;
2523
- if (start < last.start) {
2524
- for (var i = last.start - start - 1; i >= 0; --i)
2525
- last.old.unshift(old[i]);
2526
- last.added += last.start - start;
2527
- last.start = start;
2528
- }
2529
- else if (last.start < start) {
2530
- oldoff = start - last.start;
2531
- added += oldoff;
2532
- }
2533
- for (var i = last.added - oldoff, e = old.length; i < e; ++i)
2534
- last.old.push(old[i]);
2535
- if (last.added < added) last.added = added;
2899
+ var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2900
+ var dtime = time - this.time;
2901
+
2902
+ if (this.compound && cur && !this.closed) {
2903
+ cur.push({start: start, added: added, old: old});
2904
+ } else if (dtime > 400 || !last || this.closed ||
2905
+ last.start > start + old.length || last.start + last.added < start) {
2906
+ this.done.push([{start: start, added: added, old: old}]);
2907
+ this.closed = false;
2908
+ } else {
2909
+ var startBefore = Math.max(0, last.start - start),
2910
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
2911
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
2912
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
2913
+ if (startBefore) last.start = start;
2914
+ last.added += added - (old.length - startBefore - endAfter);
2536
2915
  }
2537
2916
  this.time = time;
2917
+ },
2918
+ startCompound: function() {
2919
+ if (!this.compound++) this.closed = true;
2920
+ },
2921
+ endCompound: function() {
2922
+ if (!--this.compound) this.closed = true;
2538
2923
  }
2539
2924
  };
2540
2925
 
@@ -2566,14 +2951,20 @@ var CodeMirror = (function() {
2566
2951
  else if (e.button & 4) return 2;
2567
2952
  }
2568
2953
 
2954
+ // Allow 3rd-party code to override event properties by adding an override
2955
+ // object to an event object.
2956
+ function e_prop(e, prop) {
2957
+ var overridden = e.override && e.override.hasOwnProperty(prop);
2958
+ return overridden ? e.override[prop] : e[prop];
2959
+ }
2960
+
2569
2961
  // Event handler registration. If disconnect is true, it'll return a
2570
2962
  // function that unregisters the handler.
2571
2963
  function connect(node, type, handler, disconnect) {
2572
2964
  if (typeof node.addEventListener == "function") {
2573
2965
  node.addEventListener(type, handler, false);
2574
2966
  if (disconnect) return function() {node.removeEventListener(type, handler, false);};
2575
- }
2576
- else {
2967
+ } else {
2577
2968
  var wrapHandler = function(event) {handler(event || window.event);};
2578
2969
  node.attachEvent("on" + type, wrapHandler);
2579
2970
  if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
@@ -2584,26 +2975,48 @@ var CodeMirror = (function() {
2584
2975
  function Delayed() {this.id = null;}
2585
2976
  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2586
2977
 
2587
- // Detect drag-and-drop
2588
- var dragAndDrop = function() {
2589
- // IE8 has ondragstart and ondrop properties, but doesn't seem to
2590
- // actually support ondragstart the way it's supposed to work.
2591
- if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2592
- var div = document.createElement('div');
2593
- return "draggable" in div;
2594
- }();
2978
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
2595
2979
 
2596
2980
  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2597
2981
  var ie = /MSIE \d/.test(navigator.userAgent);
2982
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
2983
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2984
+ var quirksMode = ie && document.documentMode == 5;
2598
2985
  var webkit = /WebKit\//.test(navigator.userAgent);
2986
+ var chrome = /Chrome\//.test(navigator.userAgent);
2987
+ var opera = /Opera\//.test(navigator.userAgent);
2988
+ var safari = /Apple Computer/.test(navigator.vendor);
2989
+ var khtml = /KHTML\//.test(navigator.userAgent);
2990
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
2991
+
2992
+ // Detect drag-and-drop
2993
+ var dragAndDrop = function() {
2994
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
2995
+ // couldn't get it to work yet.
2996
+ if (ie_lt9) return false;
2997
+ var div = document.createElement('div');
2998
+ return "draggable" in div || "dragDrop" in div;
2999
+ }();
2599
3000
 
2600
- var lineSep = "\n";
2601
3001
  // Feature-detect whether newlines in textareas are converted to \r\n
2602
- (function () {
3002
+ var lineSep = function () {
2603
3003
  var te = document.createElement("textarea");
2604
3004
  te.value = "foo\nbar";
2605
- if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2606
- }());
3005
+ if (te.value.indexOf("\r") > -1) return "\r\n";
3006
+ return "\n";
3007
+ }();
3008
+
3009
+ // For a reason I have yet to figure out, some browsers disallow
3010
+ // word wrapping between certain characters *only* if a new inline
3011
+ // element is started between them. This makes it hard to reliably
3012
+ // measure the position of things, since that requires inserting an
3013
+ // extra span. This terribly fragile set of regexps matches the
3014
+ // character combinations that suffer from this phenomenon on the
3015
+ // various browsers.
3016
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
3017
+ if (gecko) spanAffectsWrapping = /$'/;
3018
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
3019
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
2607
3020
 
2608
3021
  // Counts the column offset in a string, taking tabs into account.
2609
3022
  // Used mostly to find indentation.
@@ -2665,6 +3078,12 @@ var CodeMirror = (function() {
2665
3078
  function eltText(node) {
2666
3079
  return node.textContent || node.innerText || node.nodeValue || "";
2667
3080
  }
3081
+ function selectInput(node) {
3082
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
3083
+ node.selectionStart = 0;
3084
+ node.selectionEnd = node.value.length;
3085
+ } else node.select();
3086
+ }
2668
3087
 
2669
3088
  // Operations on {line, ch} objects.
2670
3089
  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
@@ -2678,24 +3097,25 @@ var CodeMirror = (function() {
2678
3097
  }
2679
3098
  // Recent (late 2011) Opera betas insert bogus newlines at the start
2680
3099
  // of the textContent, so we strip those.
2681
- if (htmlEscape("a") == "\na")
3100
+ if (htmlEscape("a") == "\na") {
2682
3101
  htmlEscape = function(str) {
2683
3102
  escapeElement.textContent = str;
2684
3103
  return escapeElement.innerHTML.slice(1);
2685
3104
  };
2686
3105
  // Some IEs don't preserve tabs through innerHTML
2687
- else if (htmlEscape("\t") != "\t")
3106
+ } else if (htmlEscape("\t") != "\t") {
2688
3107
  htmlEscape = function(str) {
2689
3108
  escapeElement.innerHTML = "";
2690
3109
  escapeElement.appendChild(document.createTextNode(str));
2691
3110
  return escapeElement.innerHTML;
2692
3111
  };
3112
+ }
2693
3113
  CodeMirror.htmlEscape = htmlEscape;
2694
3114
 
2695
3115
  // Used to position the cursor after an undo/redo by finding the
2696
3116
  // last edited character.
2697
3117
  function editEnd(from, to) {
2698
- if (!to) return from ? from.length : 0;
3118
+ if (!to) return 0;
2699
3119
  if (!from) return to.length;
2700
3120
  for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2701
3121
  if (from.charAt(i) != to.charAt(j)) break;
@@ -2743,10 +3163,10 @@ var CodeMirror = (function() {
2743
3163
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
2744
3164
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
2745
3165
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
2746
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
2747
- 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
2748
- 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
2749
- 63233: "Down", 63302: "Insert", 63272: "Delete"};
3166
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
3167
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
3168
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
3169
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
2750
3170
  CodeMirror.keyNames = keyNames;
2751
3171
  (function() {
2752
3172
  // Number keys