codemirror-rails 2.32 → 2.33

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 (50) hide show
  1. data/codemirror-rails.gemspec +1 -1
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +355 -349
  4. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +6 -5
  5. data/vendor/assets/javascripts/codemirror/modes/clike.js +6 -2
  6. data/vendor/assets/javascripts/codemirror/modes/clojure.js +1 -1
  7. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +3 -3
  8. data/vendor/assets/javascripts/codemirror/modes/css.js +52 -2
  9. data/vendor/assets/javascripts/codemirror/modes/ecl.js +1 -1
  10. data/vendor/assets/javascripts/codemirror/modes/gfm.js +4 -3
  11. data/vendor/assets/javascripts/codemirror/modes/go.js +2 -2
  12. data/vendor/assets/javascripts/codemirror/modes/groovy.js +1 -1
  13. data/vendor/assets/javascripts/codemirror/modes/haskell.js +2 -2
  14. data/vendor/assets/javascripts/codemirror/modes/haxe.js +4 -4
  15. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +3 -3
  16. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +1 -1
  17. data/vendor/assets/javascripts/codemirror/modes/javascript.js +2 -2
  18. data/vendor/assets/javascripts/codemirror/modes/less.js +94 -60
  19. data/vendor/assets/javascripts/codemirror/modes/markdown.js +4 -4
  20. data/vendor/assets/javascripts/codemirror/modes/ntriples.js +4 -4
  21. data/vendor/assets/javascripts/codemirror/modes/ocaml.js +1 -1
  22. data/vendor/assets/javascripts/codemirror/modes/pascal.js +1 -1
  23. data/vendor/assets/javascripts/codemirror/modes/perl.js +71 -71
  24. data/vendor/assets/javascripts/codemirror/modes/php.js +3 -3
  25. data/vendor/assets/javascripts/codemirror/modes/pig.js +3 -3
  26. data/vendor/assets/javascripts/codemirror/modes/python.js +1 -1
  27. data/vendor/assets/javascripts/codemirror/modes/scheme.js +1 -1
  28. data/vendor/assets/javascripts/codemirror/modes/shell.js +1 -1
  29. data/vendor/assets/javascripts/codemirror/modes/sieve.js +156 -0
  30. data/vendor/assets/javascripts/codemirror/modes/smalltalk.js +2 -2
  31. data/vendor/assets/javascripts/codemirror/modes/smarty.js +2 -2
  32. data/vendor/assets/javascripts/codemirror/modes/stex.js +1 -1
  33. data/vendor/assets/javascripts/codemirror/modes/tiki.js +4 -4
  34. data/vendor/assets/javascripts/codemirror/modes/velocity.js +1 -1
  35. data/vendor/assets/javascripts/codemirror/modes/verilog.js +1 -1
  36. data/vendor/assets/javascripts/codemirror/modes/xml.js +2 -2
  37. data/vendor/assets/javascripts/codemirror/modes/xquery.js +7 -4
  38. data/vendor/assets/javascripts/codemirror/utils/dialog.js +4 -1
  39. data/vendor/assets/javascripts/codemirror/utils/formatting.js +1 -1
  40. data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +3 -3
  41. data/vendor/assets/javascripts/codemirror/utils/loadmode.js +1 -1
  42. data/vendor/assets/javascripts/codemirror/utils/multiplex.js +1 -1
  43. data/vendor/assets/javascripts/codemirror/utils/pig-hint.js +1 -1
  44. data/vendor/assets/javascripts/codemirror/utils/runmode.js +8 -4
  45. data/vendor/assets/javascripts/codemirror/utils/search.js +4 -4
  46. data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +13 -11
  47. data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +89 -68
  48. data/vendor/assets/javascripts/codemirror/utils/xml-hint.js +8 -8
  49. data/vendor/assets/stylesheets/codemirror.css +15 -11
  50. metadata +13 -13
@@ -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', 'Robin Bühler']
6
+ s.authors = ['Nathan Fixler']
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.32'
4
- CODEMIRROR_VERSION = '2.32'
3
+ VERSION = '2.33'
4
+ CODEMIRROR_VERSION = '2.33'
5
5
  end
6
6
  end
@@ -1,11 +1,12 @@
1
- // CodeMirror version 2.32
1
+ // CodeMirror version 2.33
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,
5
5
  // some utilities are defined.
6
6
 
7
7
  // CodeMirror is the only global var we claim
8
- var CodeMirror = (function() {
8
+ window.CodeMirror = (function() {
9
+ "use strict";
9
10
  // This is the function that produces an editor instance. Its
10
11
  // closure is used to store the editor state.
11
12
  function CodeMirror(place, givenOptions) {
@@ -15,38 +16,33 @@ var CodeMirror = (function() {
15
16
  if (defaults.hasOwnProperty(opt))
16
17
  options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
17
18
 
19
+ var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em");
20
+ input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
21
+ // Wraps and hides input textarea
22
+ var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
23
+ // The empty scrollbar content, used solely for managing the scrollbar thumb.
24
+ var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner");
25
+ // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
26
+ var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar");
27
+ // DIVs containing the selection and the actual code
28
+ var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1");
29
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
30
+ var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden");
31
+ // Used to measure text size
32
+ var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;");
33
+ var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0");
34
+ var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter");
35
+ // Moved around its parent to cover visible view
36
+ var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative");
37
+ // Set to the height of the text, causes scrolling
38
+ var sizer = elt("div", [mover], null, "position: relative");
39
+ // Provides scrolling
40
+ var scroller = elt("div", [sizer], "CodeMirror-scroll");
41
+ scroller.setAttribute("tabIndex", "-1");
18
42
  // The element in which the editor lives.
19
- var wrapper = document.createElement("div");
20
- wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
21
- // This mess creates the base DOM structure for the editor.
22
- wrapper.innerHTML =
23
- '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
24
- '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
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.
29
- '<div class="CodeMirror-scroll" tabindex="-1">' +
30
- '<div style="position: relative">' + // Set to the height of the text, causes scrolling
31
- '<div style="position: relative">' + // Moved around its parent to cover visible view
32
- '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
33
- // Provides positioning relative to (visible) text origin
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: 0px; overflow: hidden; visibility: hidden;"></div>' +
37
- '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
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
40
- '</div></div></div></div></div>';
43
+ var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""));
41
44
  if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
42
- // I've never seen more elegant code in my life.
43
- var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
44
- scroller = wrapper.lastChild, code = scroller.firstChild,
45
- mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
46
- lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
47
- cursor = measure.nextSibling, widthForcer = cursor.nextSibling,
48
- selectionDiv = widthForcer.nextSibling, lineDiv = selectionDiv.nextSibling,
49
- scrollbar = inputDiv.nextSibling, scrollbarInner = scrollbar.firstChild;
45
+
50
46
  themeChanged(); keyMapChanged();
51
47
  // Needed to hide big blue blinking cursor on Mobile Safari
52
48
  if (ios) input.style.width = "0px";
@@ -58,20 +54,17 @@ var CodeMirror = (function() {
58
54
  // Needed to handle Tab key in KHTML
59
55
  if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
60
56
 
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
- }
57
+ // Check for OS X >= 10.7. This has transparent scrollbars, so the
58
+ // overlaying of one scrollbar with another won't work. This is a
59
+ // temporary hack to simply turn off the overlay scrollbar. See
60
+ // issue #727.
61
+ if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; }
62
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
63
+ else if (ie_lt8) scrollbar.style.minWidth = "18px";
71
64
 
72
65
  // Check for problem with IE innerHTML not working when we have a
73
66
  // P (or similar) parent node.
74
- try { stringWidth("x"); }
67
+ try { charWidth(); }
75
68
  catch (e) {
76
69
  if (e.message.match(/runtime/i))
77
70
  e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
@@ -92,7 +85,7 @@ var CodeMirror = (function() {
92
85
  var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
93
86
  // Selection-related flags. shiftSelecting obviously tracks
94
87
  // whether the user is holding shift.
95
- var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText,
88
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText,
96
89
  overwrite = false, suppressEdits = false;
97
90
  // Variables used by startOperation/endOperation to track what
98
91
  // happened during the operation.
@@ -105,8 +98,10 @@ var CodeMirror = (function() {
105
98
  var bracketHighlighted;
106
99
  // Tracks the maximum line length so that the horizontal scrollbar
107
100
  // can be kept static when scrolling.
108
- var maxLine = "", updateMaxLine = false, maxLineChanged = true;
101
+ var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true;
109
102
  var tabCache = {};
103
+ var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
104
+ var goalColumn = null;
110
105
 
111
106
  // Initialize the content.
112
107
  operation(function(){setValue(options.value || ""); updateInput = false;})();
@@ -120,12 +115,13 @@ var CodeMirror = (function() {
120
115
  // which point we can't mess with it anymore. Context menu is
121
116
  // handled in onMouseDown for Gecko.
122
117
  if (!gecko) connect(scroller, "contextmenu", onContextMenu);
123
- connect(scroller, "scroll", onScroll);
124
- connect(scrollbar, "scroll", onScroll);
118
+ connect(scroller, "scroll", onScrollMain);
119
+ connect(scrollbar, "scroll", onScrollBar);
125
120
  connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);});
126
- connect(scroller, "mousewheel", onMouseWheel);
127
- connect(scroller, "DOMMouseScroll", onMouseWheel);
128
- connect(window, "resize", function() {updateDisplay(true);});
121
+ var resizeHandler = connect(window, "resize", function() {
122
+ if (wrapper.parentNode) updateDisplay(true);
123
+ else resizeHandler();
124
+ }, true);
129
125
  connect(input, "keyup", operation(onKeyUp));
130
126
  connect(input, "input", fastPoll);
131
127
  connect(input, "keydown", operation(onKeyDown));
@@ -133,12 +129,12 @@ var CodeMirror = (function() {
133
129
  connect(input, "focus", onFocus);
134
130
  connect(input, "blur", onBlur);
135
131
 
132
+ function drag_(e) {
133
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
134
+ e_stop(e);
135
+ }
136
136
  if (options.dragDrop) {
137
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
138
  connect(scroller, "dragenter", drag_);
143
139
  connect(scroller, "dragover", drag_);
144
140
  connect(scroller, "drop", operation(onDrop));
@@ -150,7 +146,7 @@ var CodeMirror = (function() {
150
146
  }));
151
147
 
152
148
  // Needed to handle Tab key in KHTML
153
- if (khtml) connect(code, "mouseup", function() {
149
+ if (khtml) connect(sizer, "mouseup", function() {
154
150
  if (document.activeElement == input) input.blur();
155
151
  focusInput();
156
152
  });
@@ -183,7 +179,8 @@ var CodeMirror = (function() {
183
179
  else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
184
180
  else if (option == "tabSize") updateDisplay(true);
185
181
  else if (option == "keyMap") keyMapChanged();
186
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
182
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" ||
183
+ option == "theme" || option == "lineNumberFormatter") {
187
184
  gutterChanged();
188
185
  updateDisplay(true);
189
186
  }
@@ -213,7 +210,7 @@ var CodeMirror = (function() {
213
210
  matchBrackets: operation(function(){matchBrackets(true);}),
214
211
  getTokenAt: operation(function(pos) {
215
212
  pos = clipPos(pos);
216
- return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
213
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch);
217
214
  }),
218
215
  getStateAfter: function(line) {
219
216
  line = clipLine(line == null ? doc.size - 1: line);
@@ -250,15 +247,16 @@ var CodeMirror = (function() {
250
247
  return line;
251
248
  },
252
249
  lineInfo: lineInfo,
250
+ getViewport: function() { return {from: showingFrom, to: showingTo};},
253
251
  addWidget: function(pos, node, scroll, vert, horiz) {
254
252
  pos = localCoords(clipPos(pos));
255
253
  var top = pos.yBot, left = pos.x;
256
254
  node.style.position = "absolute";
257
- code.appendChild(node);
255
+ sizer.appendChild(node);
258
256
  if (vert == "over") top = pos.y;
259
257
  else if (vert == "near") {
260
258
  var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
261
- hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
259
+ hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft();
262
260
  if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
263
261
  top = pos.y - node.offsetHeight;
264
262
  if (left + node.offsetWidth > hspace)
@@ -267,11 +265,11 @@ var CodeMirror = (function() {
267
265
  node.style.top = (top + paddingTop()) + "px";
268
266
  node.style.left = node.style.right = "";
269
267
  if (horiz == "right") {
270
- left = code.clientWidth - node.offsetWidth;
268
+ left = sizer.clientWidth - node.offsetWidth;
271
269
  node.style.right = "0px";
272
270
  } else {
273
271
  if (horiz == "left") left = 0;
274
- else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
272
+ else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2;
275
273
  node.style.left = (left + paddingLeft()) + "px";
276
274
  }
277
275
  if (scroll)
@@ -339,7 +337,7 @@ var CodeMirror = (function() {
339
337
  },
340
338
  scrollTo: function(x, y) {
341
339
  if (x != null) scroller.scrollLeft = x;
342
- if (y != null) scrollbar.scrollTop = y;
340
+ if (y != null) scrollbar.scrollTop = scroller.scrollTop = y;
343
341
  updateDisplay([]);
344
342
  },
345
343
  getScrollInfo: function() {
@@ -353,6 +351,7 @@ var CodeMirror = (function() {
353
351
  }
354
352
  if (width != null) wrapper.style.width = interpret(width);
355
353
  if (height != null) scroller.style.height = interpret(height);
354
+ instance.refresh();
356
355
  },
357
356
 
358
357
  operation: function(f){return operation(f)();},
@@ -387,25 +386,30 @@ var CodeMirror = (function() {
387
386
  return text.join(lineSep || "\n");
388
387
  }
389
388
 
390
- function onScroll(e) {
391
- if (scroller.scrollTop) {
392
- scrollbar.scrollTop += scroller.scrollTop;
393
- scroller.scrollTop = 0;
389
+ function onScrollBar(e) {
390
+ if (scrollbar.scrollTop != lastScrollTop) {
391
+ lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
392
+ updateDisplay([]);
394
393
  }
395
- if (lastScrollTop != scrollbar.scrollTop || lastScrollLeft != scroller.scrollLeft) {
396
- lastScrollTop = scrollbar.scrollTop;
397
- lastScrollLeft = scroller.scrollLeft;
394
+ }
395
+
396
+ function onScrollMain(e) {
397
+ if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
398
+ gutter.style.left = scroller.scrollLeft + "px";
399
+ if (scroller.scrollTop != lastScrollTop) {
400
+ lastScrollTop = scroller.scrollTop;
401
+ if (scrollbar.scrollTop != lastScrollTop)
402
+ scrollbar.scrollTop = lastScrollTop;
398
403
  updateDisplay([]);
399
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
400
- if (options.onScroll) options.onScroll(instance);
401
404
  }
405
+ if (options.onScroll) options.onScroll(instance);
402
406
  }
403
407
 
404
408
  function onMouseDown(e) {
405
409
  setShift(e_prop(e, "shiftKey"));
406
410
  // Check whether this is a click in a widget
407
411
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
408
- if (n.parentNode == code && n != mover) return;
412
+ if (n.parentNode == sizer && n != mover) return;
409
413
 
410
414
  // See if this is a click in the gutter
411
415
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
@@ -448,21 +452,21 @@ var CodeMirror = (function() {
448
452
  setSelectionUser(word.from, word.to);
449
453
  } else { lastClick = {time: now, pos: start}; }
450
454
 
455
+ function dragEnd(e2) {
456
+ if (webkit) scroller.draggable = false;
457
+ draggingText = false;
458
+ up(); drop();
459
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
460
+ e_preventDefault(e2);
461
+ setCursor(start.line, start.ch, true);
462
+ focusInput();
463
+ }
464
+ }
451
465
  var last = start, going;
452
466
  if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
453
467
  !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
454
468
  // Let the drag handler handle this.
455
469
  if (webkit) scroller.draggable = true;
456
- function dragEnd(e2) {
457
- if (webkit) scroller.draggable = false;
458
- draggingText = false;
459
- up(); drop();
460
- if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
461
- e_preventDefault(e2);
462
- setCursor(start.line, start.ch, true);
463
- focusInput();
464
- }
465
- }
466
470
  var up = connect(document, "mouseup", operation(dragEnd), true);
467
471
  var drop = connect(scroller, "drop", operation(dragEnd), true);
468
472
  draggingText = true;
@@ -525,11 +529,12 @@ var CodeMirror = (function() {
525
529
  }
526
530
  function onDrop(e) {
527
531
  if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
528
- e.preventDefault();
532
+ e_preventDefault(e);
529
533
  var pos = posFromMouse(e, true), files = e.dataTransfer.files;
530
534
  if (!pos || options.readOnly) return;
531
535
  if (files && files.length && window.FileReader && window.File) {
532
- function loadFile(file, i) {
536
+ var n = files.length, text = Array(n), read = 0;
537
+ var loadFile = function(file, i) {
533
538
  var reader = new FileReader;
534
539
  reader.onload = function() {
535
540
  text[i] = reader.result;
@@ -542,8 +547,7 @@ var CodeMirror = (function() {
542
547
  }
543
548
  };
544
549
  reader.readAsText(file);
545
- }
546
- var n = files.length, text = Array(n), read = 0;
550
+ };
547
551
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
548
552
  } else {
549
553
  // Don't do a replace if the drop happened inside of the selected text.
@@ -566,10 +570,10 @@ var CodeMirror = (function() {
566
570
  function onDragStart(e) {
567
571
  var txt = getSelection();
568
572
  e.dataTransfer.setData("Text", txt);
569
-
573
+
570
574
  // Use dummy image instead of default browsers image.
571
575
  if (gecko || chrome || opera) {
572
- var img = document.createElement('img');
576
+ var img = elt('img');
573
577
  img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
574
578
  e.dataTransfer.setDragImage(img, 0, 0);
575
579
  }
@@ -594,6 +598,7 @@ var CodeMirror = (function() {
594
598
  }
595
599
  return true;
596
600
  }
601
+ var maybeTransition;
597
602
  function handleKeyBinding(e) {
598
603
  // Handle auto keymap transitions
599
604
  var startMap = getKeyMap(options.keyMap), next = startMap.auto;
@@ -640,7 +645,7 @@ var CodeMirror = (function() {
640
645
  return handled;
641
646
  }
642
647
 
643
- var lastStoppedKey = null, maybeTransition;
648
+ var lastStoppedKey = null;
644
649
  function onKeyDown(e) {
645
650
  if (!focused) onFocus();
646
651
  if (ie && e.keyCode == 27) { e.returnValue = false; }
@@ -703,42 +708,6 @@ var CodeMirror = (function() {
703
708
  setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
704
709
  }
705
710
 
706
- function chopDelta(delta) {
707
- // Make sure we always scroll a little bit for any nonzero delta.
708
- if (delta > 0.0 && delta < 1.0) return 1;
709
- else if (delta > -1.0 && delta < 0.0) return -1;
710
- else return Math.round(delta);
711
- }
712
-
713
- function onMouseWheel(e) {
714
- var deltaX = 0, deltaY = 0;
715
- if (e.type == "DOMMouseScroll") { // Firefox
716
- var delta = -e.detail * 8.0;
717
- if (e.axis == e.HORIZONTAL_AXIS) deltaX = delta;
718
- else if (e.axis == e.VERTICAL_AXIS) deltaY = delta;
719
- } else if (e.wheelDeltaX !== undefined && e.wheelDeltaY !== undefined) { // WebKit
720
- deltaX = e.wheelDeltaX / 3.0;
721
- deltaY = e.wheelDeltaY / 3.0;
722
- } else if (e.wheelDelta !== undefined) { // IE or Opera
723
- deltaY = e.wheelDelta / 3.0;
724
- }
725
-
726
- var scrolled = false;
727
- deltaX = chopDelta(deltaX);
728
- deltaY = chopDelta(deltaY);
729
- if ((deltaX > 0 && scroller.scrollLeft > 0) ||
730
- (deltaX < 0 && scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth)) {
731
- scroller.scrollLeft -= deltaX;
732
- scrolled = true;
733
- }
734
- if ((deltaY > 0 && scrollbar.scrollTop > 0) ||
735
- (deltaY < 0 && scrollbar.scrollTop + scrollbar.clientHeight < scrollbar.scrollHeight)) {
736
- scrollbar.scrollTop -= deltaY;
737
- scrolled = true;
738
- }
739
- if (scrolled) e_stop(e);
740
- }
741
-
742
711
  // Replace the range from from to to by the strings in newText.
743
712
  // Afterwards, set the selection to selFrom, selTo.
744
713
  function updateLines(from, to, newText, selFrom, selTo) {
@@ -771,7 +740,7 @@ var CodeMirror = (function() {
771
740
 
772
741
  function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
773
742
  if (suppressEdits) return;
774
- var recomputeMaxLength = false, maxLineLength = maxLine.length;
743
+ var recomputeMaxLength = false, maxLineLength = maxLine.text.length;
775
744
  if (!options.lineWrapping)
776
745
  doc.iter(from.line, to.line + 1, function(line) {
777
746
  if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
@@ -831,7 +800,7 @@ var CodeMirror = (function() {
831
800
  doc.iter(from.line, from.line + newText.length, function(line) {
832
801
  var l = line.text;
833
802
  if (!line.hidden && l.length > maxLineLength) {
834
- maxLine = l; maxLineLength = l.length; maxLineChanged = true;
803
+ maxLine = line; maxLineLength = l.length; maxLineChanged = true;
835
804
  recomputeMaxLength = false;
836
805
  }
837
806
  });
@@ -867,46 +836,43 @@ var CodeMirror = (function() {
867
836
 
868
837
  function needsScrollbar() {
869
838
  var realHeight = doc.height * textHeight() + 2 * paddingTop();
870
- return realHeight - 1 > scroller.offsetHeight ? realHeight : false;
839
+ return realHeight * .99 > scroller.offsetHeight ? realHeight : false;
871
840
  }
872
841
 
873
842
  function updateVerticalScroll(scrollTop) {
874
843
  var scrollHeight = needsScrollbar();
875
844
  scrollbar.style.display = scrollHeight ? "block" : "none";
876
845
  if (scrollHeight) {
877
- scrollbarInner.style.height = scrollHeight + "px";
878
- scrollbar.style.height = scroller.offsetHeight + "px";
879
- if (scrollTop != null) scrollbar.scrollTop = scrollTop;
846
+ scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px";
847
+ scrollbar.style.height = scroller.clientHeight + "px";
848
+ if (scrollTop != null) {
849
+ scrollbar.scrollTop = scroller.scrollTop = scrollTop;
850
+ // 'Nudge' the scrollbar to work around a Webkit bug where,
851
+ // in some situations, we'd end up with a scrollbar that
852
+ // reported its scrollTop (and looked) as expected, but
853
+ // *behaved* as if it was still in a previous state (i.e.
854
+ // couldn't scroll up, even though it appeared to be at the
855
+ // bottom).
856
+ if (webkit) setTimeout(function() {
857
+ if (scrollbar.scrollTop != scrollTop) return;
858
+ scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1);
859
+ scrollbar.scrollTop = scrollTop;
860
+ }, 0);
861
+ }
862
+ } else {
863
+ sizer.style.minHeight = "";
880
864
  }
881
865
  // Position the mover div to align with the current virtual scroll position
882
- mover.style.top = (displayOffset * textHeight() - scrollbar.scrollTop) + "px";
883
- }
884
-
885
- // On Mac OS X Lion and up, detect whether the mouse is plugged in by measuring
886
- // the width of a div with a scrollbar in it. If the width is <= 1, then
887
- // the mouse isn't plugged in and scrollbars should overlap the content.
888
- function overlapScrollbars() {
889
- var tmpSb = document.createElement('div'),
890
- tmpSbInner = document.createElement('div');
891
- tmpSb.className = "CodeMirror-scrollbar";
892
- tmpSb.style.cssText = "position: absolute; left: -9999px; height: 100px;";
893
- tmpSbInner.className = "CodeMirror-scrollbar-inner";
894
- tmpSbInner.style.height = "200px";
895
- tmpSb.appendChild(tmpSbInner);
896
-
897
- document.body.appendChild(tmpSb);
898
- var result = (tmpSb.offsetWidth <= 1);
899
- document.body.removeChild(tmpSb);
900
- return result;
866
+ mover.style.top = displayOffset * textHeight() + "px";
901
867
  }
902
868
 
903
869
  function computeMaxLength() {
904
- var maxLineLength = 0;
905
- maxLine = ""; maxLineChanged = true;
906
- doc.iter(0, doc.size, function(line) {
870
+ maxLine = getLine(0); maxLineChanged = true;
871
+ var maxLineLength = maxLine.text.length;
872
+ doc.iter(1, doc.size, function(line) {
907
873
  var l = line.text;
908
874
  if (!line.hidden && l.length > maxLineLength) {
909
- maxLineLength = l.length; maxLine = l;
875
+ maxLineLength = l.length; maxLine = line;
910
876
  }
911
877
  });
912
878
  updateMaxLine = false;
@@ -957,7 +923,6 @@ var CodeMirror = (function() {
957
923
  return getRange(sel.from, sel.to, lineSep);
958
924
  }
959
925
 
960
- var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
961
926
  function slowPoll() {
962
927
  if (pollingFast) return;
963
928
  poll.set(options.pollInterval, function() {
@@ -1006,7 +971,7 @@ var CodeMirror = (function() {
1006
971
  if (!posEq(sel.from, sel.to)) {
1007
972
  prevInput = "";
1008
973
  input.value = getSelection();
1009
- selectInput(input);
974
+ if (focused) selectInput(input);
1010
975
  } else if (user) prevInput = input.value = "";
1011
976
  }
1012
977
 
@@ -1014,16 +979,23 @@ var CodeMirror = (function() {
1014
979
  if (options.readOnly != "nocursor") input.focus();
1015
980
  }
1016
981
 
1017
- function scrollEditorIntoView() {
1018
- var rect = cursor.getBoundingClientRect();
1019
- // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
1020
- if (ie && rect.top == rect.bottom) return;
1021
- var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
1022
- if (rect.top < 0 || rect.bottom > winH) scrollCursorIntoView();
1023
- }
1024
982
  function scrollCursorIntoView() {
1025
983
  var coords = calculateCursorCoords();
1026
- return scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
984
+ scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
985
+ if (!focused) return;
986
+ var box = sizer.getBoundingClientRect(), doScroll = null;
987
+ if (coords.y + box.top < 0) doScroll = true;
988
+ else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
989
+ if (doScroll != null) {
990
+ var hidden = cursor.style.display == "none";
991
+ if (hidden) {
992
+ cursor.style.display = "";
993
+ cursor.style.left = coords.x + "px";
994
+ cursor.style.top = (coords.y - displayOffset) + "px";
995
+ }
996
+ cursor.scrollIntoView(doScroll);
997
+ if (hidden) cursor.style.display = "none";
998
+ }
1027
999
  }
1028
1000
  function calculateCursorCoords() {
1029
1001
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
@@ -1031,17 +1003,16 @@ var CodeMirror = (function() {
1031
1003
  return {x: x, y: cursor.y, yBot: cursor.yBot};
1032
1004
  }
1033
1005
  function scrollIntoView(x1, y1, x2, y2) {
1034
- var scrollPos = calculateScrollPos(x1, y1, x2, y2), scrolled = false;
1035
- if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft; scrolled = true;}
1036
- if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scrollPos.scrollTop; scrolled = true;}
1037
- if (scrolled && options.onScroll) options.onScroll(instance);
1006
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2);
1007
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;}
1008
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;}
1038
1009
  }
1039
1010
  function calculateScrollPos(x1, y1, x2, y2) {
1040
1011
  var pl = paddingLeft(), pt = paddingTop();
1041
1012
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
1042
1013
  var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
1043
- var docBottom = scroller.scrollHeight;
1044
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;;
1014
+ var docBottom = needsScrollbar() || Infinity;
1015
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
1045
1016
  if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1046
1017
  else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
1047
1018
 
@@ -1113,6 +1084,10 @@ var CodeMirror = (function() {
1113
1084
  // This is just a bogus formula that detects when the editor is
1114
1085
  // resized or the font size changes.
1115
1086
  if (different) lastSizeC = scroller.clientHeight + th;
1087
+ if (from != showingFrom || to != showingTo && options.onViewportChange)
1088
+ setTimeout(function(){
1089
+ if (options.onViewportChange) options.onViewportChange(instance, from, to);
1090
+ });
1116
1091
  showingFrom = from; showingTo = to;
1117
1092
  displayOffset = heightAtLine(doc, from);
1118
1093
 
@@ -1125,6 +1100,10 @@ var CodeMirror = (function() {
1125
1100
  function checkHeights() {
1126
1101
  var curNode = lineDiv.firstChild, heightChanged = false;
1127
1102
  doc.iter(showingFrom, showingTo, function(line) {
1103
+ // Work around bizarro IE7 bug where, sometimes, our curNode
1104
+ // is magically replaced with a new node in the DOM, leaving
1105
+ // us with a reference to an orphan (nextSibling-less) node.
1106
+ if (!curNode) return;
1128
1107
  if (!line.hidden) {
1129
1108
  var height = Math.round(curNode.offsetHeight / th) || 1;
1130
1109
  if (line.height != height) {
@@ -1137,16 +1116,7 @@ var CodeMirror = (function() {
1137
1116
  return heightChanged;
1138
1117
  }
1139
1118
 
1140
- if (options.lineWrapping) {
1141
- checkHeights();
1142
- var scrollHeight = needsScrollbar();
1143
- var shouldHaveScrollbar = scrollHeight ? "block" : "none";
1144
- if (scrollbar.style.display != shouldHaveScrollbar) {
1145
- scrollbar.style.display = shouldHaveScrollbar;
1146
- if (scrollHeight) scrollbarInner.style.height = scrollHeight + "px";
1147
- checkHeights();
1148
- }
1149
- }
1119
+ if (options.lineWrapping) checkHeights();
1150
1120
 
1151
1121
  gutter.style.display = gutterDisplay;
1152
1122
  if (different || gutterDirty) {
@@ -1183,14 +1153,14 @@ var CodeMirror = (function() {
1183
1153
  }
1184
1154
 
1185
1155
  function patchDisplay(from, to, intact) {
1156
+ function killNode(node) {
1157
+ var tmp = node.nextSibling;
1158
+ node.parentNode.removeChild(node);
1159
+ return tmp;
1160
+ }
1186
1161
  // The first pass removes the DOM nodes that aren't intact.
1187
- if (!intact.length) lineDiv.innerHTML = "";
1162
+ if (!intact.length) removeChildren(lineDiv);
1188
1163
  else {
1189
- function killNode(node) {
1190
- var tmp = node.nextSibling;
1191
- node.parentNode.removeChild(node);
1192
- return tmp;
1193
- }
1194
1164
  var domPos = 0, curNode = lineDiv.firstChild, n;
1195
1165
  for (var i = 0; i < intact.length; ++i) {
1196
1166
  var cur = intact[i];
@@ -1201,21 +1171,20 @@ var CodeMirror = (function() {
1201
1171
  }
1202
1172
  // This pass fills in the lines that actually changed.
1203
1173
  var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
1204
- var scratch = document.createElement("div");
1205
1174
  doc.iter(from, to, function(line) {
1206
1175
  if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
1207
1176
  if (!nextIntact || nextIntact.from > j) {
1208
- if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
1177
+ if (line.hidden) var lineElement = elt("pre");
1209
1178
  else {
1210
- var html = '<pre' + (line.className ? ' class="' + line.className + '"' : '') + '>'
1211
- + line.getHTML(makeTab) + '</pre>';
1179
+ var lineElement = line.getElement(makeTab);
1180
+ if (line.className) lineElement.className = line.className;
1212
1181
  // Kludge to make sure the styled element lies behind the selection (by z-index)
1213
- if (line.bgClassName)
1214
- html = '<div style="position: relative"><pre class="' + line.bgClassName +
1215
- '" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>' + html + "</div>";
1182
+ if (line.bgClassName) {
1183
+ var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2");
1184
+ lineElement = elt("div", [pre, lineElement], null, "position: relative");
1185
+ }
1216
1186
  }
1217
- scratch.innerHTML = html;
1218
- lineDiv.insertBefore(scratch.firstChild, curNode);
1187
+ lineDiv.insertBefore(lineElement, curNode);
1219
1188
  } else {
1220
1189
  curNode = curNode.nextSibling;
1221
1190
  }
@@ -1227,10 +1196,10 @@ var CodeMirror = (function() {
1227
1196
  if (!options.gutter && !options.lineNumbers) return;
1228
1197
  var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
1229
1198
  gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
1230
- var html = [], i = showingFrom, normalNode;
1199
+ var fragment = document.createDocumentFragment(), i = showingFrom, normalNode;
1231
1200
  doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
1232
1201
  if (line.hidden) {
1233
- html.push("<pre></pre>");
1202
+ fragment.appendChild(elt("pre"));
1234
1203
  } else {
1235
1204
  var marker = line.gutterMarker;
1236
1205
  var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
@@ -1238,15 +1207,18 @@ var CodeMirror = (function() {
1238
1207
  text = marker.text.replace("%N%", text != null ? text : "");
1239
1208
  else if (text == null)
1240
1209
  text = "\u00a0";
1241
- html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
1242
- for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
1243
- html.push("</pre>");
1210
+ var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style));
1211
+ markerElement.innerHTML = text;
1212
+ for (var j = 1; j < line.height; ++j) {
1213
+ markerElement.appendChild(elt("br"));
1214
+ markerElement.appendChild(document.createTextNode("\u00a0"));
1215
+ }
1244
1216
  if (!marker) normalNode = i;
1245
1217
  }
1246
1218
  ++i;
1247
1219
  });
1248
1220
  gutter.style.display = "none";
1249
- gutterText.innerHTML = html.join("");
1221
+ removeChildrenAndAdd(gutterText, fragment);
1250
1222
  // Make sure scrolling doesn't cause number gutter size to pop
1251
1223
  if (normalNode != null && options.lineNumbers) {
1252
1224
  var node = gutterText.childNodes[normalNode - showingFrom];
@@ -1274,15 +1246,15 @@ var CodeMirror = (function() {
1274
1246
  cursor.style.display = "";
1275
1247
  selectionDiv.style.display = "none";
1276
1248
  } else {
1277
- var sameLine = fromPos.y == toPos.y, html = "";
1249
+ var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment();
1278
1250
  var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1279
1251
  var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1280
- function add(left, top, right, height) {
1252
+ var add = function(left, top, right, height) {
1281
1253
  var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
1282
1254
  : "right: " + right + "px";
1283
- html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
1284
- 'px; top: ' + top + 'px; ' + rstyle + '; height: ' + height + 'px"></div>';
1285
- }
1255
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
1256
+ "px; top: " + top + "px; " + rstyle + "; height: " + height + "px"));
1257
+ };
1286
1258
  if (sel.from.ch && fromPos.y >= 0) {
1287
1259
  var right = sameLine ? clientWidth - toPos.x : 0;
1288
1260
  add(fromPos.x, fromPos.y, right, th);
@@ -1293,7 +1265,7 @@ var CodeMirror = (function() {
1293
1265
  add(0, middleStart, 0, middleHeight);
1294
1266
  if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
1295
1267
  add(0, toPos.y, clientWidth - toPos.x, th);
1296
- selectionDiv.innerHTML = html;
1268
+ removeChildrenAndAdd(selectionDiv, fragment);
1297
1269
  cursor.style.display = "none";
1298
1270
  selectionDiv.style.display = "";
1299
1271
  }
@@ -1425,13 +1397,16 @@ var CodeMirror = (function() {
1425
1397
  else replaceRange("", sel.from, findPosH(dir, unit));
1426
1398
  userSelChange = true;
1427
1399
  }
1428
- var goalColumn = null;
1429
1400
  function moveV(dir, unit) {
1430
1401
  var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1431
1402
  if (goalColumn != null) pos.x = goalColumn;
1432
- if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1433
- else if (unit == "line") dist = textHeight();
1434
- var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1403
+ if (unit == "page") {
1404
+ var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1405
+ var target = coordsChar(pos.x, pos.y + screen * dir);
1406
+ } else if (unit == "line") {
1407
+ var th = textHeight();
1408
+ var target = coordsChar(pos.x, pos.y + .5 * th + dir * th);
1409
+ }
1435
1410
  if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
1436
1411
  setCursor(target.line, target.ch, true);
1437
1412
  goalColumn = pos.x;
@@ -1440,10 +1415,15 @@ var CodeMirror = (function() {
1440
1415
  function findWordAt(pos) {
1441
1416
  var line = getLine(pos.line).text;
1442
1417
  var start = pos.ch, end = pos.ch;
1443
- var check = isWordChar(line.charAt(start < line.length ? start : start - 1)) ?
1444
- isWordChar : function(ch) {return !isWordChar(ch);};
1445
- while (start > 0 && check(line.charAt(start - 1))) --start;
1446
- while (end < line.length && check(line.charAt(end))) ++end;
1418
+ if (line) {
1419
+ if (pos.after === false || end == line.length) --start; else ++end;
1420
+ var startChar = line.charAt(start);
1421
+ var check = isWordChar(startChar) ? isWordChar :
1422
+ /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
1423
+ function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
1424
+ while (start > 0 && check(line.charAt(start - 1))) --start;
1425
+ while (end < line.length && check(line.charAt(end))) ++end;
1426
+ }
1447
1427
  return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
1448
1428
  }
1449
1429
  function selectLine(line) {
@@ -1482,7 +1462,8 @@ var CodeMirror = (function() {
1482
1462
  for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1483
1463
  while (pos < indentation) {++pos; indentString += " ";}
1484
1464
 
1485
- replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1465
+ if (indentString != curSpaceString)
1466
+ replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1486
1467
  }
1487
1468
 
1488
1469
  function loadMode() {
@@ -1506,14 +1487,12 @@ var CodeMirror = (function() {
1506
1487
  var guess = Math.ceil(line.text.length / perLine) || 1;
1507
1488
  if (guess != 1) updateLineHeight(line, guess);
1508
1489
  });
1509
- lineSpace.style.width = code.style.width = "";
1510
- widthForcer.style.left = "";
1490
+ lineSpace.style.minWidth = widthForcer.style.left = "";
1511
1491
  } else {
1512
1492
  wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1513
- maxLine = ""; maxLineChanged = true;
1493
+ computeMaxLength();
1514
1494
  doc.iter(0, doc.size, function(line) {
1515
1495
  if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1516
- if (line.text.length > maxLine.length) maxLine = line.text;
1517
1496
  });
1518
1497
  }
1519
1498
  changes.push({from: 0, to: doc.size});
@@ -1521,8 +1500,9 @@ var CodeMirror = (function() {
1521
1500
  function makeTab(col) {
1522
1501
  var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
1523
1502
  if (cached) return cached;
1524
- for (var str = '<span class="cm-tab">', i = 0; i < w; ++i) str += " ";
1525
- return (tabCache[w] = {html: str + "</span>", width: w});
1503
+ for (var str = "", i = 0; i < w; ++i) str += " ";
1504
+ var span = elt("span", str, "cm-tab");
1505
+ return (tabCache[w] = {element: span, width: w});
1526
1506
  }
1527
1507
  function themeChanged() {
1528
1508
  scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
@@ -1641,11 +1621,10 @@ var CodeMirror = (function() {
1641
1621
  if (line.hidden != hidden) {
1642
1622
  line.hidden = hidden;
1643
1623
  if (!options.lineWrapping) {
1644
- var l = line.text;
1645
- if (hidden && l.length == maxLine.length) {
1624
+ if (hidden && line.text.length == maxLine.text.length) {
1646
1625
  updateMaxLine = true;
1647
- } else if (!hidden && l.length > maxLine.length) {
1648
- maxLine = l; updateMaxLine = false;
1626
+ } else if (!hidden && line.text.length > maxLine.text.length) {
1627
+ maxLine = line; updateMaxLine = false;
1649
1628
  }
1650
1629
  }
1651
1630
  updateLineHeight(line, hidden ? 0 : 1);
@@ -1677,11 +1656,6 @@ var CodeMirror = (function() {
1677
1656
  markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1678
1657
  }
1679
1658
 
1680
- function stringWidth(str) {
1681
- measure.innerHTML = "<pre><span>x</span></pre>";
1682
- measure.firstChild.firstChild.firstChild.nodeValue = str;
1683
- return measure.firstChild.firstChild.offsetWidth || 10;
1684
- }
1685
1659
  // These are used to go from pixel positions to character
1686
1660
  // positions, taking varying character widths into account.
1687
1661
  function charFromX(line, x) {
@@ -1711,19 +1685,18 @@ var CodeMirror = (function() {
1711
1685
  }
1712
1686
  }
1713
1687
 
1714
- var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16);
1715
1688
  function measureLine(line, ch) {
1716
1689
  if (ch == 0) return {top: 0, left: 0};
1717
1690
  var wbr = options.lineWrapping && ch < line.text.length &&
1718
1691
  spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
1719
- measure.innerHTML = "<pre>" + line.getHTML(makeTab, ch, tempId, wbr) + "</pre>";
1720
- var elt = document.getElementById(tempId);
1721
- var top = elt.offsetTop, left = elt.offsetLeft;
1692
+ var pre = line.getElement(makeTab, ch, wbr);
1693
+ removeChildrenAndAdd(measure, pre);
1694
+ var anchor = pre.anchor;
1695
+ var top = anchor.offsetTop, left = anchor.offsetLeft;
1722
1696
  // Older IEs report zero offsets for spans directly after a wrap
1723
1697
  if (ie && top == 0 && left == 0) {
1724
- var backup = document.createElement("span");
1725
- backup.innerHTML = "x";
1726
- elt.parentNode.insertBefore(backup, elt.nextSibling);
1698
+ var backup = elt("span", "x");
1699
+ anchor.parentNode.insertBefore(backup, anchor.nextSibling);
1727
1700
  top = backup.offsetTop;
1728
1701
  }
1729
1702
  return {top: top, left: left};
@@ -1740,17 +1713,19 @@ var CodeMirror = (function() {
1740
1713
  }
1741
1714
  // Coords must be lineSpace-local
1742
1715
  function coordsChar(x, y) {
1743
- if (y < 0) y = 0;
1744
1716
  var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1717
+ if (heightPos < 0) return {line: 0, ch: 0};
1745
1718
  var lineNo = lineAtHeight(doc, heightPos);
1746
1719
  if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1747
1720
  var lineObj = getLine(lineNo), text = lineObj.text;
1748
1721
  var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1749
1722
  if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1723
+ var wrongLine = false;
1750
1724
  function getX(len) {
1751
1725
  var sp = measureLine(lineObj, len);
1752
1726
  if (tw) {
1753
1727
  var off = Math.round(sp.top / th);
1728
+ wrongLine = off != innerOff;
1754
1729
  return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1755
1730
  }
1756
1731
  return sp.left;
@@ -1769,9 +1744,12 @@ var CodeMirror = (function() {
1769
1744
  if (estX < x) {from = estimated; fromX = estX;}
1770
1745
  // Do a binary search between these bounds.
1771
1746
  for (;;) {
1772
- if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
1747
+ if (to - from <= 1) {
1748
+ var after = x - fromX < toX - x;
1749
+ return {line: lineNo, ch: after ? from : to, after: after};
1750
+ }
1773
1751
  var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1774
- if (middleX > x) {to = middle; toX = middleX;}
1752
+ if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; }
1775
1753
  else {from = middle; fromX = middleX;}
1776
1754
  }
1777
1755
  }
@@ -1780,26 +1758,32 @@ var CodeMirror = (function() {
1780
1758
  return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1781
1759
  }
1782
1760
 
1783
- var cachedHeight, cachedHeightFor, measureText;
1761
+ var cachedHeight, cachedHeightFor, measurePre;
1784
1762
  function textHeight() {
1785
- if (measureText == null) {
1786
- measureText = "<pre>";
1787
- for (var i = 0; i < 49; ++i) measureText += "x<br/>";
1788
- measureText += "x</pre>";
1763
+ if (measurePre == null) {
1764
+ measurePre = elt("pre");
1765
+ for (var i = 0; i < 49; ++i) {
1766
+ measurePre.appendChild(document.createTextNode("x"));
1767
+ measurePre.appendChild(elt("br"));
1768
+ }
1769
+ measurePre.appendChild(document.createTextNode("x"));
1789
1770
  }
1790
1771
  var offsetHeight = lineDiv.clientHeight;
1791
1772
  if (offsetHeight == cachedHeightFor) return cachedHeight;
1792
1773
  cachedHeightFor = offsetHeight;
1793
- measure.innerHTML = measureText;
1774
+ removeChildrenAndAdd(measure, measurePre.cloneNode(true));
1794
1775
  cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1795
- measure.innerHTML = "";
1776
+ removeChildren(measure);
1796
1777
  return cachedHeight;
1797
1778
  }
1798
1779
  var cachedWidth, cachedWidthFor = 0;
1799
1780
  function charWidth() {
1800
1781
  if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1801
1782
  cachedWidthFor = scroller.clientWidth;
1802
- return (cachedWidth = stringWidth("x"));
1783
+ var anchor = elt("span", "x");
1784
+ var pre = elt("pre", [anchor]);
1785
+ removeChildrenAndAdd(measure, pre);
1786
+ return (cachedWidth = anchor.offsetWidth || 10);
1803
1787
  }
1804
1788
  function paddingTop() {return lineSpace.offsetTop;}
1805
1789
  function paddingLeft() {return lineSpace.offsetLeft;}
@@ -1860,7 +1844,7 @@ var CodeMirror = (function() {
1860
1844
  cursor.style.visibility = "";
1861
1845
  blinker = setInterval(function() {
1862
1846
  cursor.style.visibility = (on = !on) ? "" : "hidden";
1863
- }, 650);
1847
+ }, options.cursorBlinkRate);
1864
1848
  }
1865
1849
 
1866
1850
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
@@ -2000,9 +1984,11 @@ var CodeMirror = (function() {
2000
1984
  function endOperation() {
2001
1985
  if (updateMaxLine) computeMaxLength();
2002
1986
  if (maxLineChanged && !options.lineWrapping) {
2003
- var cursorWidth = widthForcer.offsetWidth, left = stringWidth(maxLine);
2004
- widthForcer.style.left = left + "px";
2005
- lineSpace.style.minWidth = (left + cursorWidth) + "px";
1987
+ var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left;
1988
+ if (!ie_lt8) {
1989
+ widthForcer.style.left = left + "px";
1990
+ lineSpace.style.minWidth = (left + cursorWidth) + "px";
1991
+ }
2006
1992
  maxLineChanged = false;
2007
1993
  }
2008
1994
  var newScrollPos, updated;
@@ -2010,13 +1996,14 @@ var CodeMirror = (function() {
2010
1996
  var coords = calculateCursorCoords();
2011
1997
  newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
2012
1998
  }
2013
- if (changes.length) updated = updateDisplay(changes, true, (newScrollPos ? newScrollPos.scrollTop : null));
2014
- else {
1999
+ if (changes.length || newScrollPos && newScrollPos.scrollTop != null)
2000
+ updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop);
2001
+ if (!updated) {
2015
2002
  if (selectionChanged) updateSelection();
2016
2003
  if (gutterDirty) updateGutter();
2017
2004
  }
2018
2005
  if (newScrollPos) scrollCursorIntoView();
2019
- if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
2006
+ if (selectionChanged) restartBlink();
2020
2007
 
2021
2008
  if (focused && !leaveInputAlone &&
2022
2009
  (updateInput === true || (updateInput !== false && selectionChanged)))
@@ -2081,11 +2068,13 @@ var CodeMirror = (function() {
2081
2068
  dragDrop: true,
2082
2069
  onChange: null,
2083
2070
  onCursorActivity: null,
2071
+ onViewportChange: null,
2084
2072
  onGutterClick: null,
2085
2073
  onHighlightComplete: null,
2086
2074
  onUpdate: null,
2087
2075
  onFocus: null, onBlur: null, onScroll: null,
2088
2076
  matchBrackets: false,
2077
+ cursorBlinkRate: 530,
2089
2078
  workTime: 100,
2090
2079
  workDelay: 200,
2091
2080
  pollInterval: 100,
@@ -2241,6 +2230,10 @@ var CodeMirror = (function() {
2241
2230
  function lookup(map) {
2242
2231
  map = getKeyMap(map);
2243
2232
  var found = map[name];
2233
+ if (found === false) {
2234
+ if (stop) stop();
2235
+ return true;
2236
+ }
2244
2237
  if (found != null && handle(found)) return true;
2245
2238
  if (map.nofallthrough) {
2246
2239
  if (stop) stop();
@@ -2268,8 +2261,15 @@ var CodeMirror = (function() {
2268
2261
  options.value = textarea.value;
2269
2262
  if (!options.tabindex && textarea.tabindex)
2270
2263
  options.tabindex = textarea.tabindex;
2271
- if (options.autofocus == null && textarea.getAttribute("autofocus") != null)
2272
- options.autofocus = true;
2264
+ // Set autofocus to true if this textarea is focused, or if it has
2265
+ // autofocus and no other element is focused.
2266
+ if (options.autofocus == null) {
2267
+ var hasFocus = document.body;
2268
+ // doc.activeElement occasionally throws on IE
2269
+ try { hasFocus = document.activeElement; } catch(e) {}
2270
+ options.autofocus = hasFocus == textarea ||
2271
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
2272
+ }
2273
2273
 
2274
2274
  function save() {textarea.value = instance.getValue();}
2275
2275
  if (textarea.form) {
@@ -2277,13 +2277,12 @@ var CodeMirror = (function() {
2277
2277
  var rmSubmit = connect(textarea.form, "submit", save, true);
2278
2278
  if (typeof textarea.form.submit == "function") {
2279
2279
  var realSubmit = textarea.form.submit;
2280
- function wrappedSubmit() {
2280
+ textarea.form.submit = function wrappedSubmit() {
2281
2281
  save();
2282
2282
  textarea.form.submit = realSubmit;
2283
2283
  textarea.form.submit();
2284
2284
  textarea.form.submit = wrappedSubmit;
2285
- }
2286
- textarea.form.submit = wrappedSubmit;
2285
+ };
2287
2286
  }
2288
2287
  }
2289
2288
 
@@ -2306,6 +2305,18 @@ var CodeMirror = (function() {
2306
2305
  return instance;
2307
2306
  };
2308
2307
 
2308
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2309
+ var ie = /MSIE \d/.test(navigator.userAgent);
2310
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
2311
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2312
+ var quirksMode = ie && document.documentMode == 5;
2313
+ var webkit = /WebKit\//.test(navigator.userAgent);
2314
+ var chrome = /Chrome\//.test(navigator.userAgent);
2315
+ var opera = /Opera\//.test(navigator.userAgent);
2316
+ var safari = /Apple Computer/.test(navigator.vendor);
2317
+ var khtml = /KHTML\//.test(navigator.userAgent);
2318
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
2319
+
2309
2320
  // Utility functions for working with state. Exported because modes
2310
2321
  // sometimes need to do this.
2311
2322
  function copyState(mode, state) {
@@ -2334,7 +2345,7 @@ var CodeMirror = (function() {
2334
2345
  StringStream.prototype = {
2335
2346
  eol: function() {return this.pos >= this.string.length;},
2336
2347
  sol: function() {return this.pos == 0;},
2337
- peek: function() {return this.string.charAt(this.pos);},
2348
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
2338
2349
  next: function() {
2339
2350
  if (this.pos < this.string.length)
2340
2351
  return this.string.charAt(this.pos++);
@@ -2365,7 +2376,7 @@ var CodeMirror = (function() {
2365
2376
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
2366
2377
  match: function(pattern, consume, caseInsensitive) {
2367
2378
  if (typeof pattern == "string") {
2368
- function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
2379
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
2369
2380
  if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
2370
2381
  if (consume !== false) this.pos += pattern.length;
2371
2382
  return true;
@@ -2444,14 +2455,22 @@ var CodeMirror = (function() {
2444
2455
  }
2445
2456
  };
2446
2457
 
2458
+ // When measuring the position of the end of a line, different
2459
+ // browsers require different approaches. If an empty span is added,
2460
+ // many browsers report bogus offsets. Of those, some (Webkit,
2461
+ // recent IE) will accept a space without moving the whole span to
2462
+ // the next line when wrapping it, others work with a zero-width
2463
+ // space.
2464
+ var eolSpanContent = " ";
2465
+ if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b";
2466
+ else if (opera) eolSpanContent = "";
2467
+
2447
2468
  // Line objects. These hold state related to a line, including
2448
2469
  // highlighting info (the styles array).
2449
2470
  function Line(text, styles) {
2450
2471
  this.styles = styles || [text, null];
2451
2472
  this.text = text;
2452
2473
  this.height = 1;
2453
- this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null;
2454
- this.stateAfter = this.parent = this.hidden = null;
2455
2474
  }
2456
2475
  Line.inheritMarks = function(text, orig) {
2457
2476
  var ln = new Line(text), mk = orig && orig.marked;
@@ -2464,7 +2483,7 @@ var CodeMirror = (function() {
2464
2483
  }
2465
2484
  }
2466
2485
  return ln;
2467
- }
2486
+ };
2468
2487
  Line.prototype = {
2469
2488
  // Replace a piece of a line, keeping the styles around it intact.
2470
2489
  replace: function(from, to_, text) {
@@ -2542,7 +2561,7 @@ var CodeMirror = (function() {
2542
2561
  if (close && omk) {
2543
2562
  for (var j = 0; j < omk.length; ++j) {
2544
2563
  var om = omk[j];
2545
- if (!om.sameSet(mark) || om.from != null) continue
2564
+ if (!om.sameSet(mark) || om.from != null) continue;
2546
2565
  if (mark.from == this.text.length && om.to == 0) {
2547
2566
  omk.splice(j, 1);
2548
2567
  mk.splice(i--, 1);
@@ -2600,8 +2619,8 @@ var CodeMirror = (function() {
2600
2619
  },
2601
2620
  // Fetch the parser token for a given character. Useful for hacks
2602
2621
  // that want to inspect the mode state (say, for completion).
2603
- getTokenAt: function(mode, state, ch) {
2604
- var txt = this.text, stream = new StringStream(txt);
2622
+ getTokenAt: function(mode, state, tabSize, ch) {
2623
+ var txt = this.text, stream = new StringStream(txt, tabSize);
2605
2624
  while (stream.pos < ch && !stream.eol()) {
2606
2625
  stream.start = stream.pos;
2607
2626
  var style = mode.token(stream, state);
@@ -2615,68 +2634,72 @@ var CodeMirror = (function() {
2615
2634
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2616
2635
  // Produces an HTML fragment for the line, taking selection,
2617
2636
  // marking, and highlighting into account.
2618
- getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) {
2619
- var html = [], first = true, col = 0;
2620
- function span_(text, style) {
2637
+ getElement: function(makeTab, wrapAt, wrapWBR) {
2638
+ var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
2639
+ var pre = elt("pre");
2640
+ function span_(html, text, style) {
2621
2641
  if (!text) return;
2622
2642
  // Work around a bug where, in some compat modes, IE ignores leading spaces
2623
2643
  if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2624
2644
  first = false;
2625
- if (text.indexOf("\t") == -1) {
2645
+ if (!specials.test(text)) {
2626
2646
  col += text.length;
2627
- var escaped = htmlEscape(text);
2647
+ var content = document.createTextNode(text);
2628
2648
  } else {
2629
- var escaped = "";
2630
- for (var pos = 0;;) {
2631
- var idx = text.indexOf("\t", pos);
2632
- if (idx == -1) {
2633
- escaped += htmlEscape(text.slice(pos));
2634
- col += text.length - pos;
2635
- break;
2636
- } else {
2637
- col += idx - pos;
2649
+ var content = document.createDocumentFragment(), pos = 0;
2650
+ while (true) {
2651
+ specials.lastIndex = pos;
2652
+ var m = specials.exec(text);
2653
+ var skipped = m ? m.index - pos : text.length - pos;
2654
+ if (skipped) {
2655
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
2656
+ col += skipped;
2657
+ }
2658
+ if (!m) break;
2659
+ pos += skipped + 1;
2660
+ if (m[0] == "\t") {
2638
2661
  var tab = makeTab(col);
2639
- escaped += htmlEscape(text.slice(pos, idx)) + tab.html;
2662
+ content.appendChild(tab.element.cloneNode(true));
2640
2663
  col += tab.width;
2641
- pos = idx + 1;
2664
+ } else {
2665
+ var token = elt("span", "\u2022", "cm-invalidchar");
2666
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
2667
+ content.appendChild(token);
2668
+ col += 1;
2642
2669
  }
2643
2670
  }
2644
2671
  }
2645
- if (style) html.push('<span class="', style, '">', escaped, "</span>");
2646
- else html.push(escaped);
2672
+ if (style) html.appendChild(elt("span", [content], style));
2673
+ else html.appendChild(content);
2647
2674
  }
2648
2675
  var span = span_;
2649
2676
  if (wrapAt != null) {
2650
- var outPos = 0, open = "<span id=\"" + wrapId + "\">";
2651
- span = function(text, style) {
2677
+ var outPos = 0, anchor = pre.anchor = elt("span");
2678
+ span = function(html, text, style) {
2652
2679
  var l = text.length;
2653
2680
  if (wrapAt >= outPos && wrapAt < outPos + l) {
2654
2681
  if (wrapAt > outPos) {
2655
- span_(text.slice(0, wrapAt - outPos), style);
2682
+ span_(html, text.slice(0, wrapAt - outPos), style);
2656
2683
  // See comment at the definition of spanAffectsWrapping
2657
- if (wrapWBR) html.push("<wbr>");
2684
+ if (wrapWBR) html.appendChild(elt("wbr"));
2658
2685
  }
2659
- html.push(open);
2686
+ html.appendChild(anchor);
2660
2687
  var cut = wrapAt - outPos;
2661
- span_(opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2662
- html.push("</span>");
2663
- if (opera) span_(text.slice(cut + 1), style);
2688
+ span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2689
+ if (opera) span_(html, text.slice(cut + 1), style);
2664
2690
  wrapAt--;
2665
2691
  outPos += l;
2666
2692
  } else {
2667
2693
  outPos += l;
2668
- span_(text, style);
2669
- // Output empty wrapper when at end of line
2670
- // (Gecko and IE8+ do strange wrapping when adding a space
2671
- // to the end of the line. Other browsers don't react well
2672
- // to zero-width spaces. So we do hideous browser sniffing
2673
- // to determine which to use.)
2674
- if (outPos == wrapAt && outPos == len)
2675
- html.push(open + (gecko || (ie && !ie_lt8) ? "&#x200b;" : " ") + "</span>");
2694
+ span_(html, text, style);
2695
+ if (outPos == wrapAt && outPos == len) {
2696
+ setTextContent(anchor, eolSpanContent);
2697
+ html.appendChild(anchor);
2698
+ }
2676
2699
  // Stop outputting HTML when gone sufficiently far beyond measure
2677
2700
  else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2678
2701
  }
2679
- }
2702
+ };
2680
2703
  }
2681
2704
 
2682
2705
  var st = this.styles, allText = this.text, marked = this.marked;
@@ -2685,20 +2708,19 @@ var CodeMirror = (function() {
2685
2708
  if (!style) return null;
2686
2709
  return "cm-" + style.replace(/ +/g, " cm-");
2687
2710
  }
2688
-
2689
2711
  if (!allText && wrapAt == null) {
2690
- span(" ");
2712
+ span(pre, " ");
2691
2713
  } else if (!marked || !marked.length) {
2692
2714
  for (var i = 0, ch = 0; ch < len; i+=2) {
2693
2715
  var str = st[i], style = st[i+1], l = str.length;
2694
2716
  if (ch + l > len) str = str.slice(0, len - ch);
2695
2717
  ch += l;
2696
- span(str, styleToClass(style));
2718
+ span(pre, str, styleToClass(style));
2697
2719
  }
2698
2720
  } else {
2699
2721
  var pos = 0, i = 0, text = "", style, sg = 0;
2700
2722
  var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2701
- function advanceMarks() {
2723
+ var advanceMarks = function() {
2702
2724
  var m;
2703
2725
  while (markpos < marked.length &&
2704
2726
  ((m = marked[markpos]).from == pos || m.from == null)) {
@@ -2712,7 +2734,7 @@ var CodeMirror = (function() {
2712
2734
  if (to == pos) marks.splice(i--, 1);
2713
2735
  else nextChange = Math.min(to, nextChange);
2714
2736
  }
2715
- }
2737
+ };
2716
2738
  var m = 0;
2717
2739
  while (pos < len) {
2718
2740
  if (nextChange == pos) advanceMarks();
@@ -2723,7 +2745,7 @@ var CodeMirror = (function() {
2723
2745
  var appliedStyle = style;
2724
2746
  for (var j = 0; j < marks.length; ++j)
2725
2747
  appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2726
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2748
+ span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2727
2749
  if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2728
2750
  pos = end;
2729
2751
  }
@@ -2731,7 +2753,7 @@ var CodeMirror = (function() {
2731
2753
  }
2732
2754
  }
2733
2755
  }
2734
- return html.join("");
2756
+ return pre;
2735
2757
  },
2736
2758
  cleanUp: function() {
2737
2759
  this.parent = null;
@@ -3038,30 +3060,18 @@ var CodeMirror = (function() {
3038
3060
 
3039
3061
  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
3040
3062
 
3041
- var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
3042
- var ie = /MSIE \d/.test(navigator.userAgent);
3043
- var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
3044
- var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
3045
- var quirksMode = ie && document.documentMode == 5;
3046
- var webkit = /WebKit\//.test(navigator.userAgent);
3047
- var chrome = /Chrome\//.test(navigator.userAgent);
3048
- var opera = /Opera\//.test(navigator.userAgent);
3049
- var safari = /Apple Computer/.test(navigator.vendor);
3050
- var khtml = /KHTML\//.test(navigator.userAgent);
3051
- var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
3052
-
3053
3063
  // Detect drag-and-drop
3054
3064
  var dragAndDrop = function() {
3055
3065
  // There is *some* kind of drag-and-drop support in IE6-8, but I
3056
3066
  // couldn't get it to work yet.
3057
3067
  if (ie_lt9) return false;
3058
- var div = document.createElement('div');
3068
+ var div = elt('div');
3059
3069
  return "draggable" in div || "dragDrop" in div;
3060
3070
  }();
3061
3071
 
3062
3072
  // Feature-detect whether newlines in textareas are converted to \r\n
3063
3073
  var lineSep = function () {
3064
- var te = document.createElement("textarea");
3074
+ var te = elt("textarea");
3065
3075
  te.value = "foo\nbar";
3066
3076
  if (te.value.indexOf("\r") > -1) return "\r\n";
3067
3077
  return "\n";
@@ -3093,11 +3103,6 @@ var CodeMirror = (function() {
3093
3103
  return n;
3094
3104
  }
3095
3105
 
3096
- function computedStyle(elt) {
3097
- if (elt.currentStyle) return elt.currentStyle;
3098
- return window.getComputedStyle(elt, null);
3099
- }
3100
-
3101
3106
  function eltOffset(node, screen) {
3102
3107
  // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
3103
3108
  // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
@@ -3132,27 +3137,28 @@ var CodeMirror = (function() {
3132
3137
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
3133
3138
  function copyPos(x) {return {line: x.line, ch: x.ch};}
3134
3139
 
3135
- var escapeElement = document.createElement("pre");
3136
- function htmlEscape(str) {
3137
- escapeElement.textContent = str;
3138
- return escapeElement.innerHTML;
3140
+ function elt(tag, content, className, style) {
3141
+ var e = document.createElement(tag);
3142
+ if (className) e.className = className;
3143
+ if (style) e.style.cssText = style;
3144
+ if (typeof content == "string") setTextContent(e, content);
3145
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
3146
+ return e;
3139
3147
  }
3140
- // Recent (late 2011) Opera betas insert bogus newlines at the start
3141
- // of the textContent, so we strip those.
3142
- if (htmlEscape("a") == "\na") {
3143
- htmlEscape = function(str) {
3144
- escapeElement.textContent = str;
3145
- return escapeElement.innerHTML.slice(1);
3146
- };
3147
- // Some IEs don't preserve tabs through innerHTML
3148
- } else if (htmlEscape("\t") != "\t") {
3149
- htmlEscape = function(str) {
3150
- escapeElement.innerHTML = "";
3151
- escapeElement.appendChild(document.createTextNode(str));
3152
- return escapeElement.innerHTML;
3153
- };
3148
+ function removeChildren(e) {
3149
+ e.innerHTML = "";
3150
+ return e;
3151
+ }
3152
+ function removeChildrenAndAdd(parent, e) {
3153
+ removeChildren(parent).appendChild(e);
3154
+ }
3155
+ function setTextContent(e, str) {
3156
+ if (ie_lt9) {
3157
+ e.innerHTML = "";
3158
+ e.appendChild(document.createTextNode(str));
3159
+ } else e.textContent = str;
3154
3160
  }
3155
- CodeMirror.htmlEscape = htmlEscape;
3161
+ CodeMirror.setTextContent = setTextContent;
3156
3162
 
3157
3163
  // Used to position the cursor after an undo/redo by finding the
3158
3164
  // last edited character.