erp_app 3.0.10 → 3.0.12

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 (129) hide show
  1. data/lib/erp_app/version.rb +1 -1
  2. data/public/javascripts/erp_app/codemirror/README.md +1 -1
  3. data/public/javascripts/erp_app/codemirror/doc/compress.html +11 -3
  4. data/public/javascripts/erp_app/codemirror/doc/docs.css +16 -5
  5. data/public/javascripts/erp_app/codemirror/doc/internals.html +6 -3
  6. data/public/javascripts/erp_app/codemirror/doc/manual.html +103 -41
  7. data/public/javascripts/erp_app/codemirror/doc/oldrelease.html +43 -5
  8. data/public/javascripts/erp_app/codemirror/doc/reporting.html +6 -3
  9. data/public/javascripts/erp_app/codemirror/doc/upgrade_v2.2.html +7 -4
  10. data/public/javascripts/erp_app/codemirror/keymap/vim.js +10 -6
  11. data/public/javascripts/erp_app/codemirror/lib/codemirror.css +16 -12
  12. data/public/javascripts/erp_app/codemirror/lib/codemirror.js +699 -787
  13. data/public/javascripts/erp_app/codemirror/lib/util/closetag.js +35 -35
  14. data/public/javascripts/erp_app/codemirror/lib/util/dialog.js +4 -1
  15. data/public/javascripts/erp_app/codemirror/lib/util/formatting.js +147 -253
  16. data/public/javascripts/erp_app/codemirror/lib/util/javascript-hint.js +3 -3
  17. data/public/javascripts/erp_app/codemirror/lib/util/loadmode.js +1 -1
  18. data/public/javascripts/erp_app/codemirror/lib/util/multiplex.js +5 -9
  19. data/public/javascripts/erp_app/codemirror/lib/util/overlay.js +3 -1
  20. data/public/javascripts/erp_app/codemirror/lib/util/pig-hint.js +1 -1
  21. data/public/javascripts/erp_app/codemirror/lib/util/runmode-standalone.js +90 -0
  22. data/public/javascripts/erp_app/codemirror/lib/util/runmode.js +8 -4
  23. data/public/javascripts/erp_app/codemirror/lib/util/search.js +4 -4
  24. data/public/javascripts/erp_app/codemirror/lib/util/searchcursor.js +13 -11
  25. data/public/javascripts/erp_app/codemirror/lib/util/simple-hint.js +89 -68
  26. data/public/javascripts/erp_app/codemirror/lib/util/xml-hint.js +8 -8
  27. data/public/javascripts/erp_app/codemirror/mode/clike/clike.js +6 -2
  28. data/public/javascripts/erp_app/codemirror/mode/clike/index.html +1 -0
  29. data/public/javascripts/erp_app/codemirror/mode/clike/scala.html +1 -0
  30. data/public/javascripts/erp_app/codemirror/mode/clojure/clojure.js +8 -9
  31. data/public/javascripts/erp_app/codemirror/mode/clojure/index.html +1 -0
  32. data/public/javascripts/erp_app/codemirror/mode/coffeescript/coffeescript.js +3 -3
  33. data/public/javascripts/erp_app/codemirror/mode/coffeescript/index.html +1 -0
  34. data/public/javascripts/erp_app/codemirror/mode/commonlisp/commonlisp.js +101 -0
  35. data/public/javascripts/erp_app/codemirror/mode/commonlisp/index.html +165 -0
  36. data/public/javascripts/erp_app/codemirror/mode/css/css.js +339 -15
  37. data/public/javascripts/erp_app/codemirror/mode/css/index.html +3 -0
  38. data/public/javascripts/erp_app/codemirror/mode/css/test.js +501 -0
  39. data/public/javascripts/erp_app/codemirror/mode/diff/index.html +1 -0
  40. data/public/javascripts/erp_app/codemirror/mode/ecl/ecl.js +1 -1
  41. data/public/javascripts/erp_app/codemirror/mode/erlang/index.html +1 -0
  42. data/public/javascripts/erp_app/codemirror/mode/gfm/gfm.js +10 -4
  43. data/public/javascripts/erp_app/codemirror/mode/gfm/index.html +1 -0
  44. data/public/javascripts/erp_app/codemirror/mode/go/go.js +2 -2
  45. data/public/javascripts/erp_app/codemirror/mode/go/index.html +1 -0
  46. data/public/javascripts/erp_app/codemirror/mode/groovy/groovy.js +1 -1
  47. data/public/javascripts/erp_app/codemirror/mode/groovy/index.html +1 -0
  48. data/public/javascripts/erp_app/codemirror/mode/haskell/haskell.js +2 -2
  49. data/public/javascripts/erp_app/codemirror/mode/haskell/index.html +1 -0
  50. data/public/javascripts/erp_app/codemirror/mode/haxe/haxe.js +4 -7
  51. data/public/javascripts/erp_app/codemirror/mode/haxe/index.html +1 -0
  52. data/public/javascripts/erp_app/codemirror/mode/htmlembedded/htmlembedded.js +8 -4
  53. data/public/javascripts/erp_app/codemirror/mode/htmlembedded/index.html +1 -0
  54. data/public/javascripts/erp_app/codemirror/mode/htmlmixed/htmlmixed.js +12 -13
  55. data/public/javascripts/erp_app/codemirror/mode/htmlmixed/index.html +1 -0
  56. data/public/javascripts/erp_app/codemirror/mode/javascript/index.html +1 -0
  57. data/public/javascripts/erp_app/codemirror/mode/javascript/javascript.js +3 -3
  58. data/public/javascripts/erp_app/codemirror/mode/jinja2/index.html +1 -0
  59. data/public/javascripts/erp_app/codemirror/mode/less/index.html +124 -2
  60. data/public/javascripts/erp_app/codemirror/mode/less/less.js +94 -60
  61. data/public/javascripts/erp_app/codemirror/mode/lua/index.html +1 -0
  62. data/public/javascripts/erp_app/codemirror/mode/markdown/index.html +3 -0
  63. data/public/javascripts/erp_app/codemirror/mode/markdown/markdown.js +134 -20
  64. data/public/javascripts/erp_app/codemirror/mode/markdown/test.js +1084 -0
  65. data/public/javascripts/erp_app/codemirror/mode/mysql/index.html +1 -0
  66. data/public/javascripts/erp_app/codemirror/mode/ntriples/index.html +1 -0
  67. data/public/javascripts/erp_app/codemirror/mode/ntriples/ntriples.js +4 -4
  68. data/public/javascripts/erp_app/codemirror/mode/ocaml/ocaml.js +1 -1
  69. data/public/javascripts/erp_app/codemirror/mode/pascal/index.html +1 -0
  70. data/public/javascripts/erp_app/codemirror/mode/pascal/pascal.js +1 -1
  71. data/public/javascripts/erp_app/codemirror/mode/perl/index.html +1 -0
  72. data/public/javascripts/erp_app/codemirror/mode/perl/perl.js +71 -71
  73. data/public/javascripts/erp_app/codemirror/mode/php/index.html +1 -0
  74. data/public/javascripts/erp_app/codemirror/mode/php/php.js +7 -9
  75. data/public/javascripts/erp_app/codemirror/mode/pig/index.html +1 -0
  76. data/public/javascripts/erp_app/codemirror/mode/pig/pig.js +3 -3
  77. data/public/javascripts/erp_app/codemirror/mode/plsql/index.html +1 -0
  78. data/public/javascripts/erp_app/codemirror/mode/properties/index.html +1 -0
  79. data/public/javascripts/erp_app/codemirror/mode/python/index.html +1 -0
  80. data/public/javascripts/erp_app/codemirror/mode/python/python.js +1 -1
  81. data/public/javascripts/erp_app/codemirror/mode/r/index.html +1 -0
  82. data/public/javascripts/erp_app/codemirror/mode/rpm/changes/index.html +1 -0
  83. data/public/javascripts/erp_app/codemirror/mode/rpm/spec/index.html +1 -0
  84. data/public/javascripts/erp_app/codemirror/mode/rst/index.html +1 -0
  85. data/public/javascripts/erp_app/codemirror/mode/ruby/index.html +1 -0
  86. data/public/javascripts/erp_app/codemirror/mode/rust/index.html +1 -0
  87. data/public/javascripts/erp_app/codemirror/mode/scheme/index.html +1 -0
  88. data/public/javascripts/erp_app/codemirror/mode/scheme/scheme.js +1 -1
  89. data/public/javascripts/erp_app/codemirror/mode/shell/shell.js +2 -2
  90. data/public/javascripts/erp_app/codemirror/mode/sieve/LICENSE +23 -0
  91. data/public/javascripts/erp_app/codemirror/mode/sieve/index.html +81 -0
  92. data/public/javascripts/erp_app/codemirror/mode/sieve/sieve.js +156 -0
  93. data/public/javascripts/erp_app/codemirror/mode/smalltalk/index.html +1 -0
  94. data/public/javascripts/erp_app/codemirror/mode/smalltalk/smalltalk.js +2 -2
  95. data/public/javascripts/erp_app/codemirror/mode/smarty/index.html +1 -0
  96. data/public/javascripts/erp_app/codemirror/mode/smarty/smarty.js +2 -2
  97. data/public/javascripts/erp_app/codemirror/mode/sparql/index.html +1 -0
  98. data/public/javascripts/erp_app/codemirror/mode/stex/index.html +3 -0
  99. data/public/javascripts/erp_app/codemirror/mode/stex/stex.js +1 -1
  100. data/public/javascripts/erp_app/codemirror/mode/stex/test.js +343 -0
  101. data/public/javascripts/erp_app/codemirror/mode/tiddlywiki/index.html +1 -0
  102. data/public/javascripts/erp_app/codemirror/mode/tiki/index.html +1 -0
  103. data/public/javascripts/erp_app/codemirror/mode/tiki/tiki.js +4 -11
  104. data/public/javascripts/erp_app/codemirror/mode/vb/index.html +1 -0
  105. data/public/javascripts/erp_app/codemirror/mode/vb/vb.js +2 -2
  106. data/public/javascripts/erp_app/codemirror/mode/vbscript/index.html +1 -0
  107. data/public/javascripts/erp_app/codemirror/mode/velocity/index.html +1 -0
  108. data/public/javascripts/erp_app/codemirror/mode/velocity/velocity.js +1 -1
  109. data/public/javascripts/erp_app/codemirror/mode/verilog/index.html +1 -0
  110. data/public/javascripts/erp_app/codemirror/mode/verilog/verilog.js +1 -1
  111. data/public/javascripts/erp_app/codemirror/mode/xml/index.html +2 -1
  112. data/public/javascripts/erp_app/codemirror/mode/xml/xml.js +2 -10
  113. data/public/javascripts/erp_app/codemirror/mode/xquery/index.html +3 -2
  114. data/public/javascripts/erp_app/codemirror/mode/xquery/xquery.js +7 -4
  115. data/public/javascripts/erp_app/codemirror/mode/yaml/index.html +1 -0
  116. data/public/javascripts/erp_app/codemirror/package.json +15 -23
  117. data/public/javascripts/erp_app/codemirror/test/driver.js +109 -22
  118. data/public/javascripts/erp_app/codemirror/test/index.html +129 -16
  119. data/public/javascripts/erp_app/codemirror/test/lint/lint.js +120 -0
  120. data/public/javascripts/erp_app/codemirror/test/lint/parse-js.js +1372 -0
  121. data/public/javascripts/erp_app/codemirror/test/mode_test.css +0 -12
  122. data/public/javascripts/erp_app/codemirror/test/mode_test.js +66 -27
  123. data/public/javascripts/erp_app/codemirror/test/phantom_driver.js +30 -0
  124. data/public/javascripts/erp_app/codemirror/test/run.js +32 -0
  125. data/public/javascripts/erp_app/codemirror/test/test.js +213 -8
  126. data/public/javascripts/erp_app/shared/dynamic_editable_grid.js +33 -27
  127. data/public/javascripts/erp_app/shared/dynamic_editable_grid_loader_panel.js +2 -1
  128. metadata +15 -3
  129. data/public/javascripts/erp_app/codemirror/mode/stex/test.html +0 -263
@@ -1,11 +1,12 @@
1
- // CodeMirror version 2.32
2
- //
1
+ // CodeMirror version 2.34
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,33 +54,21 @@ 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
- }
71
-
72
- // Check for problem with IE innerHTML not working when we have a
73
- // P (or similar) parent node.
74
- try { stringWidth("x"); }
75
- catch (e) {
76
- if (e.message.match(/runtime/i))
77
- e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
78
- throw e;
79
- }
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";
80
64
 
81
65
  // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
82
66
  var poll = new Delayed(), highlight = new Delayed(), blinker;
83
67
 
84
68
  // mode holds a mode API object. doc is the tree of Line objects,
85
- // work an array of lines that should be parsed, and history the
86
- // undo history (instance of History constructor).
87
- var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
69
+ // frontier is the point up to which the content has been parsed,
70
+ // and history the undo history (instance of History constructor).
71
+ var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), frontier = 0, focused;
88
72
  loadMode();
89
73
  // The selection. These are always maintained to point at valid
90
74
  // positions. Inverted is used to remember that the user is
@@ -92,11 +76,11 @@ var CodeMirror = (function() {
92
76
  var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
93
77
  // Selection-related flags. shiftSelecting obviously tracks
94
78
  // whether the user is holding shift.
95
- var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText,
79
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText,
96
80
  overwrite = false, suppressEdits = false;
97
81
  // Variables used by startOperation/endOperation to track what
98
82
  // happened during the operation.
99
- var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
83
+ var updateInput, userSelChange, changes, textChanged, selectionChanged,
100
84
  gutterDirty, callbacks;
101
85
  // Current visible range (may be bigger than the view window).
102
86
  var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
@@ -105,8 +89,9 @@ var CodeMirror = (function() {
105
89
  var bracketHighlighted;
106
90
  // Tracks the maximum line length so that the horizontal scrollbar
107
91
  // can be kept static when scrolling.
108
- var maxLine = "", updateMaxLine = false, maxLineChanged = true;
109
- var tabCache = {};
92
+ var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true;
93
+ var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
94
+ var goalColumn = null;
110
95
 
111
96
  // Initialize the content.
112
97
  operation(function(){setValue(options.value || ""); updateInput = false;})();
@@ -120,12 +105,13 @@ var CodeMirror = (function() {
120
105
  // which point we can't mess with it anymore. Context menu is
121
106
  // handled in onMouseDown for Gecko.
122
107
  if (!gecko) connect(scroller, "contextmenu", onContextMenu);
123
- connect(scroller, "scroll", onScroll);
124
- connect(scrollbar, "scroll", onScroll);
108
+ connect(scroller, "scroll", onScrollMain);
109
+ connect(scrollbar, "scroll", onScrollBar);
125
110
  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);});
111
+ var resizeHandler = connect(window, "resize", function() {
112
+ if (wrapper.parentNode) updateDisplay(true);
113
+ else resizeHandler();
114
+ }, true);
129
115
  connect(input, "keyup", operation(onKeyUp));
130
116
  connect(input, "input", fastPoll);
131
117
  connect(input, "keydown", operation(onKeyDown));
@@ -133,12 +119,12 @@ var CodeMirror = (function() {
133
119
  connect(input, "focus", onFocus);
134
120
  connect(input, "blur", onBlur);
135
121
 
122
+ function drag_(e) {
123
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
124
+ e_stop(e);
125
+ }
136
126
  if (options.dragDrop) {
137
127
  connect(scroller, "dragstart", onDragStart);
138
- function drag_(e) {
139
- if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
140
- e_stop(e);
141
- }
142
128
  connect(scroller, "dragenter", drag_);
143
129
  connect(scroller, "dragover", drag_);
144
130
  connect(scroller, "drop", operation(onDrop));
@@ -150,7 +136,7 @@ var CodeMirror = (function() {
150
136
  }));
151
137
 
152
138
  // Needed to handle Tab key in KHTML
153
- if (khtml) connect(code, "mouseup", function() {
139
+ if (khtml) connect(sizer, "mouseup", function() {
154
140
  if (document.activeElement == input) input.blur();
155
141
  focusInput();
156
142
  });
@@ -183,12 +169,14 @@ var CodeMirror = (function() {
183
169
  else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
184
170
  else if (option == "tabSize") updateDisplay(true);
185
171
  else if (option == "keyMap") keyMapChanged();
186
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
172
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" ||
173
+ option == "theme" || option == "lineNumberFormatter") {
187
174
  gutterChanged();
188
175
  updateDisplay(true);
189
176
  }
190
177
  },
191
178
  getOption: function(option) {return options[option];},
179
+ getMode: function() {return mode;},
192
180
  undo: operation(undo),
193
181
  redo: operation(redo),
194
182
  indentLine: operation(function(n, dir) {
@@ -207,13 +195,23 @@ var CodeMirror = (function() {
207
195
  history.undone = histData.undone;
208
196
  },
209
197
  getHistory: function() {
210
- history.time = 0;
211
- return {done: history.done.concat([]), undone: history.undone.concat([])};
198
+ function cp(arr) {
199
+ for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
200
+ nw.push(nwelt = []);
201
+ for (var j = 0, elt = arr[i]; j < elt.length; ++j) {
202
+ var old = [], cur = elt[j];
203
+ nwelt.push({start: cur.start, added: cur.added, old: old});
204
+ for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
205
+ }
206
+ }
207
+ return nw;
208
+ }
209
+ return {done: cp(history.done), undone: cp(history.undone)};
212
210
  },
213
211
  matchBrackets: operation(function(){matchBrackets(true);}),
214
212
  getTokenAt: operation(function(pos) {
215
213
  pos = clipPos(pos);
216
- return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
214
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch);
217
215
  }),
218
216
  getStateAfter: function(line) {
219
217
  line = clipLine(line == null ? doc.size - 1: line);
@@ -250,15 +248,16 @@ var CodeMirror = (function() {
250
248
  return line;
251
249
  },
252
250
  lineInfo: lineInfo,
251
+ getViewport: function() { return {from: showingFrom, to: showingTo};},
253
252
  addWidget: function(pos, node, scroll, vert, horiz) {
254
253
  pos = localCoords(clipPos(pos));
255
254
  var top = pos.yBot, left = pos.x;
256
255
  node.style.position = "absolute";
257
- code.appendChild(node);
256
+ sizer.appendChild(node);
258
257
  if (vert == "over") top = pos.y;
259
258
  else if (vert == "near") {
260
259
  var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
261
- hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
260
+ hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft();
262
261
  if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
263
262
  top = pos.y - node.offsetHeight;
264
263
  if (left + node.offsetWidth > hspace)
@@ -267,11 +266,11 @@ var CodeMirror = (function() {
267
266
  node.style.top = (top + paddingTop()) + "px";
268
267
  node.style.left = node.style.right = "";
269
268
  if (horiz == "right") {
270
- left = code.clientWidth - node.offsetWidth;
269
+ left = sizer.clientWidth - node.offsetWidth;
271
270
  node.style.right = "0px";
272
271
  } else {
273
272
  if (horiz == "left") left = 0;
274
- else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
273
+ else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2;
275
274
  node.style.left = (left + paddingLeft()) + "px";
276
275
  }
277
276
  if (scroll)
@@ -339,7 +338,7 @@ var CodeMirror = (function() {
339
338
  },
340
339
  scrollTo: function(x, y) {
341
340
  if (x != null) scroller.scrollLeft = x;
342
- if (y != null) scrollbar.scrollTop = y;
341
+ if (y != null) scrollbar.scrollTop = scroller.scrollTop = y;
343
342
  updateDisplay([]);
344
343
  },
345
344
  getScrollInfo: function() {
@@ -353,6 +352,7 @@ var CodeMirror = (function() {
353
352
  }
354
353
  if (width != null) wrapper.style.width = interpret(width);
355
354
  if (height != null) scroller.style.height = interpret(height);
355
+ instance.refresh();
356
356
  },
357
357
 
358
358
  operation: function(f){return operation(f)();},
@@ -375,6 +375,12 @@ var CodeMirror = (function() {
375
375
  for (var n = line; n; n = n.parent) n.height += diff;
376
376
  }
377
377
 
378
+ function lineContent(line, wrapAt) {
379
+ if (!line.styles)
380
+ line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize);
381
+ return line.getContent(options.tabSize, wrapAt, options.lineWrapping);
382
+ }
383
+
378
384
  function setValue(code) {
379
385
  var top = {line: 0, ch: 0};
380
386
  updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
@@ -387,25 +393,30 @@ var CodeMirror = (function() {
387
393
  return text.join(lineSep || "\n");
388
394
  }
389
395
 
390
- function onScroll(e) {
391
- if (scroller.scrollTop) {
392
- scrollbar.scrollTop += scroller.scrollTop;
393
- scroller.scrollTop = 0;
396
+ function onScrollBar(e) {
397
+ if (scrollbar.scrollTop != lastScrollTop) {
398
+ lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
399
+ updateDisplay([]);
394
400
  }
395
- if (lastScrollTop != scrollbar.scrollTop || lastScrollLeft != scroller.scrollLeft) {
396
- lastScrollTop = scrollbar.scrollTop;
397
- lastScrollLeft = scroller.scrollLeft;
401
+ }
402
+
403
+ function onScrollMain(e) {
404
+ if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
405
+ gutter.style.left = scroller.scrollLeft + "px";
406
+ if (scroller.scrollTop != lastScrollTop) {
407
+ lastScrollTop = scroller.scrollTop;
408
+ if (scrollbar.scrollTop != lastScrollTop)
409
+ scrollbar.scrollTop = lastScrollTop;
398
410
  updateDisplay([]);
399
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
400
- if (options.onScroll) options.onScroll(instance);
401
411
  }
412
+ if (options.onScroll) options.onScroll(instance);
402
413
  }
403
414
 
404
415
  function onMouseDown(e) {
405
416
  setShift(e_prop(e, "shiftKey"));
406
417
  // Check whether this is a click in a widget
407
418
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
408
- if (n.parentNode == code && n != mover) return;
419
+ if (n.parentNode == sizer && n != mover) return;
409
420
 
410
421
  // See if this is a click in the gutter
411
422
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
@@ -448,21 +459,21 @@ var CodeMirror = (function() {
448
459
  setSelectionUser(word.from, word.to);
449
460
  } else { lastClick = {time: now, pos: start}; }
450
461
 
462
+ function dragEnd(e2) {
463
+ if (webkit) scroller.draggable = false;
464
+ draggingText = false;
465
+ up(); drop();
466
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
467
+ e_preventDefault(e2);
468
+ setCursor(start.line, start.ch, true);
469
+ focusInput();
470
+ }
471
+ }
451
472
  var last = start, going;
452
473
  if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
453
474
  !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
454
475
  // Let the drag handler handle this.
455
476
  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
477
  var up = connect(document, "mouseup", operation(dragEnd), true);
467
478
  var drop = connect(scroller, "drop", operation(dragEnd), true);
468
479
  draggingText = true;
@@ -525,11 +536,12 @@ var CodeMirror = (function() {
525
536
  }
526
537
  function onDrop(e) {
527
538
  if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
528
- e.preventDefault();
539
+ e_preventDefault(e);
529
540
  var pos = posFromMouse(e, true), files = e.dataTransfer.files;
530
541
  if (!pos || options.readOnly) return;
531
542
  if (files && files.length && window.FileReader && window.File) {
532
- function loadFile(file, i) {
543
+ var n = files.length, text = Array(n), read = 0;
544
+ var loadFile = function(file, i) {
533
545
  var reader = new FileReader;
534
546
  reader.onload = function() {
535
547
  text[i] = reader.result;
@@ -542,8 +554,7 @@ var CodeMirror = (function() {
542
554
  }
543
555
  };
544
556
  reader.readAsText(file);
545
- }
546
- var n = files.length, text = Array(n), read = 0;
557
+ };
547
558
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
548
559
  } else {
549
560
  // Don't do a replace if the drop happened inside of the selected text.
@@ -566,13 +577,10 @@ var CodeMirror = (function() {
566
577
  function onDragStart(e) {
567
578
  var txt = getSelection();
568
579
  e.dataTransfer.setData("Text", txt);
569
-
580
+
570
581
  // Use dummy image instead of default browsers image.
571
- if (gecko || chrome || opera) {
572
- var img = document.createElement('img');
573
- img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
574
- e.dataTransfer.setDragImage(img, 0, 0);
575
- }
582
+ if (e.dataTransfer.setDragImage)
583
+ e.dataTransfer.setDragImage(elt('img'), 0, 0);
576
584
  }
577
585
 
578
586
  function doHandleBinding(bound, dropShift) {
@@ -594,6 +602,7 @@ var CodeMirror = (function() {
594
602
  }
595
603
  return true;
596
604
  }
605
+ var maybeTransition;
597
606
  function handleKeyBinding(e) {
598
607
  // Handle auto keymap transitions
599
608
  var startMap = getKeyMap(options.keyMap), next = startMap.auto;
@@ -605,10 +614,11 @@ var CodeMirror = (function() {
605
614
  }, 50);
606
615
 
607
616
  var name = keyNames[e_prop(e, "keyCode")], handled = false;
617
+ var flipCtrlCmd = opera && mac;
608
618
  if (name == null || e.altGraphKey) return false;
609
619
  if (e_prop(e, "altKey")) name = "Alt-" + name;
610
- if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
611
- if (e_prop(e, "metaKey")) name = "Cmd-" + name;
620
+ if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
621
+ if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
612
622
 
613
623
  var stopped = false;
614
624
  function stop() { stopped = true; }
@@ -640,7 +650,7 @@ var CodeMirror = (function() {
640
650
  return handled;
641
651
  }
642
652
 
643
- var lastStoppedKey = null, maybeTransition;
653
+ var lastStoppedKey = null;
644
654
  function onKeyDown(e) {
645
655
  if (!focused) onFocus();
646
656
  if (ie && e.keyCode == 27) { e.returnValue = false; }
@@ -684,7 +694,6 @@ var CodeMirror = (function() {
684
694
  focused = true;
685
695
  if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
686
696
  scroller.className += " CodeMirror-focused";
687
- if (!leaveInputAlone) resetInput(true);
688
697
  }
689
698
  slowPoll();
690
699
  restartBlink();
@@ -703,53 +712,20 @@ var CodeMirror = (function() {
703
712
  setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
704
713
  }
705
714
 
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
715
  // Replace the range from from to to by the strings in newText.
743
716
  // Afterwards, set the selection to selFrom, selTo.
744
717
  function updateLines(from, to, newText, selFrom, selTo) {
745
718
  if (suppressEdits) return;
719
+ var old = [];
720
+ doc.iter(from.line, to.line + 1, function(line) {
721
+ old.push(newHL(line.text, line.markedSpans));
722
+ });
746
723
  if (history) {
747
- var old = [];
748
- doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
749
724
  history.addChange(from.line, newText.length, old);
750
725
  while (history.done.length > options.undoDepth) history.done.shift();
751
726
  }
752
- updateLinesNoUndo(from, to, newText, selFrom, selTo);
727
+ var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
728
+ updateLinesNoUndo(from, to, lines, selFrom, selTo);
753
729
  }
754
730
  function unredoHelper(from, to) {
755
731
  if (!from.length) return;
@@ -757,11 +733,12 @@ var CodeMirror = (function() {
757
733
  for (var i = set.length - 1; i >= 0; i -= 1) {
758
734
  var change = set[i];
759
735
  var replaced = [], end = change.start + change.added;
760
- doc.iter(change.start, end, function(line) { replaced.push(line.text); });
736
+ doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
761
737
  out.push({start: change.start, added: change.old.length, old: replaced});
762
738
  var pos = {line: change.start + change.old.length - 1,
763
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
764
- updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
739
+ ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))};
740
+ updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length},
741
+ change.old, pos, pos);
765
742
  }
766
743
  updateInput = true;
767
744
  to.push(out);
@@ -769,95 +746,86 @@ var CodeMirror = (function() {
769
746
  function undo() {unredoHelper(history.done, history.undone);}
770
747
  function redo() {unredoHelper(history.undone, history.done);}
771
748
 
772
- function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
749
+ function updateLinesNoUndo(from, to, lines, selFrom, selTo) {
773
750
  if (suppressEdits) return;
774
- var recomputeMaxLength = false, maxLineLength = maxLine.length;
751
+ var recomputeMaxLength = false, maxLineLength = maxLine.text.length;
775
752
  if (!options.lineWrapping)
776
753
  doc.iter(from.line, to.line + 1, function(line) {
777
754
  if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
778
755
  });
779
- if (from.line != to.line || newText.length > 1) gutterDirty = true;
756
+ if (from.line != to.line || lines.length > 1) gutterDirty = true;
780
757
 
781
758
  var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
782
- // First adjust the line structure, taking some care to leave highlighting intact.
783
- if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
759
+ var lastHL = lst(lines);
760
+
761
+ // First adjust the line structure
762
+ if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
784
763
  // This is a whole-line replace. Treated specially to make
785
764
  // sure line objects move the way they are supposed to.
786
765
  var added = [], prevLine = null;
787
- if (from.line) {
788
- prevLine = getLine(from.line - 1);
789
- prevLine.fixMarkEnds(lastLine);
790
- } else lastLine.fixMarkStarts();
791
- for (var i = 0, e = newText.length - 1; i < e; ++i)
792
- added.push(Line.inheritMarks(newText[i], prevLine));
766
+ for (var i = 0, e = lines.length - 1; i < e; ++i)
767
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
768
+ lastLine.update(lastLine.text, hlSpans(lastHL));
793
769
  if (nlines) doc.remove(from.line, nlines, callbacks);
794
770
  if (added.length) doc.insert(from.line, added);
795
771
  } else if (firstLine == lastLine) {
796
- if (newText.length == 1)
797
- firstLine.replace(from.ch, to.ch, newText[0]);
798
- else {
799
- lastLine = firstLine.split(to.ch, newText[newText.length-1]);
800
- firstLine.replace(from.ch, null, newText[0]);
801
- firstLine.fixMarkEnds(lastLine);
802
- var added = [];
803
- for (var i = 1, e = newText.length - 1; i < e; ++i)
804
- added.push(Line.inheritMarks(newText[i], firstLine));
805
- added.push(lastLine);
772
+ if (lines.length == 1) {
773
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), hlSpans(lines[0]));
774
+ } else {
775
+ for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
776
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
777
+ added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL)));
778
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
806
779
  doc.insert(from.line + 1, added);
807
780
  }
808
- } else if (newText.length == 1) {
809
- firstLine.replace(from.ch, null, newText[0]);
810
- lastLine.replace(null, to.ch, "");
811
- firstLine.append(lastLine);
781
+ } else if (lines.length == 1) {
782
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), hlSpans(lines[0]));
812
783
  doc.remove(from.line + 1, nlines, callbacks);
813
784
  } else {
814
785
  var added = [];
815
- firstLine.replace(from.ch, null, newText[0]);
816
- lastLine.replace(null, to.ch, newText[newText.length-1]);
817
- firstLine.fixMarkEnds(lastLine);
818
- for (var i = 1, e = newText.length - 1; i < e; ++i)
819
- added.push(Line.inheritMarks(newText[i], firstLine));
786
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
787
+ lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
788
+ for (var i = 1, e = lines.length - 1; i < e; ++i)
789
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
820
790
  if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
821
791
  doc.insert(from.line + 1, added);
822
792
  }
823
793
  if (options.lineWrapping) {
824
794
  var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
825
- doc.iter(from.line, from.line + newText.length, function(line) {
795
+ doc.iter(from.line, from.line + lines.length, function(line) {
826
796
  if (line.hidden) return;
827
797
  var guess = Math.ceil(line.text.length / perLine) || 1;
828
798
  if (guess != line.height) updateLineHeight(line, guess);
829
799
  });
830
800
  } else {
831
- doc.iter(from.line, from.line + newText.length, function(line) {
801
+ doc.iter(from.line, from.line + lines.length, function(line) {
832
802
  var l = line.text;
833
803
  if (!line.hidden && l.length > maxLineLength) {
834
- maxLine = l; maxLineLength = l.length; maxLineChanged = true;
804
+ maxLine = line; maxLineLength = l.length; maxLineChanged = true;
835
805
  recomputeMaxLength = false;
836
806
  }
837
807
  });
838
808
  if (recomputeMaxLength) updateMaxLine = true;
839
809
  }
840
810
 
841
- // Add these lines to the work array, so that they will be
842
- // highlighted. Adjust work lines if lines were added/removed.
843
- var newWork = [], lendiff = newText.length - nlines - 1;
844
- for (var i = 0, l = work.length; i < l; ++i) {
845
- var task = work[i];
846
- if (task < from.line) newWork.push(task);
847
- else if (task > to.line) newWork.push(task + lendiff);
848
- }
849
- var hlEnd = from.line + Math.min(newText.length, 500);
850
- highlightLines(from.line, hlEnd);
851
- newWork.push(hlEnd);
852
- work = newWork;
853
- startWorker(100);
811
+ // Adjust frontier, schedule worker
812
+ frontier = Math.min(frontier, from.line);
813
+ startWorker(400);
814
+
815
+ var lendiff = lines.length - nlines - 1;
854
816
  // Remember that these lines changed, for updating the display
855
817
  changes.push({from: from.line, to: to.line + 1, diff: lendiff});
856
- var changeObj = {from: from, to: to, text: newText};
857
- if (textChanged) {
858
- for (var cur = textChanged; cur.next; cur = cur.next) {}
859
- cur.next = changeObj;
860
- } else textChanged = changeObj;
818
+ if (options.onChange) {
819
+ // Normalize lines to contain only strings, since that's what
820
+ // the change event handler expects
821
+ for (var i = 0; i < lines.length; ++i)
822
+ if (typeof lines[i] != "string") lines[i] = lines[i].text;
823
+ var changeObj = {from: from, to: to, text: lines};
824
+ if (textChanged) {
825
+ for (var cur = textChanged; cur.next; cur = cur.next) {}
826
+ cur.next = changeObj;
827
+ } else textChanged = changeObj;
828
+ }
861
829
 
862
830
  // Update the selection
863
831
  function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
@@ -867,46 +835,43 @@ var CodeMirror = (function() {
867
835
 
868
836
  function needsScrollbar() {
869
837
  var realHeight = doc.height * textHeight() + 2 * paddingTop();
870
- return realHeight - 1 > scroller.offsetHeight ? realHeight : false;
838
+ return realHeight * .99 > scroller.offsetHeight ? realHeight : false;
871
839
  }
872
840
 
873
841
  function updateVerticalScroll(scrollTop) {
874
842
  var scrollHeight = needsScrollbar();
875
843
  scrollbar.style.display = scrollHeight ? "block" : "none";
876
844
  if (scrollHeight) {
877
- scrollbarInner.style.height = scrollHeight + "px";
878
- scrollbar.style.height = scroller.offsetHeight + "px";
879
- if (scrollTop != null) scrollbar.scrollTop = scrollTop;
845
+ scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px";
846
+ scrollbar.style.height = scroller.clientHeight + "px";
847
+ if (scrollTop != null) {
848
+ scrollbar.scrollTop = scroller.scrollTop = scrollTop;
849
+ // 'Nudge' the scrollbar to work around a Webkit bug where,
850
+ // in some situations, we'd end up with a scrollbar that
851
+ // reported its scrollTop (and looked) as expected, but
852
+ // *behaved* as if it was still in a previous state (i.e.
853
+ // couldn't scroll up, even though it appeared to be at the
854
+ // bottom).
855
+ if (webkit) setTimeout(function() {
856
+ if (scrollbar.scrollTop != scrollTop) return;
857
+ scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1);
858
+ scrollbar.scrollTop = scrollTop;
859
+ }, 0);
860
+ }
861
+ } else {
862
+ sizer.style.minHeight = "";
880
863
  }
881
864
  // 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;
865
+ mover.style.top = displayOffset * textHeight() + "px";
901
866
  }
902
867
 
903
868
  function computeMaxLength() {
904
- var maxLineLength = 0;
905
- maxLine = ""; maxLineChanged = true;
906
- doc.iter(0, doc.size, function(line) {
869
+ maxLine = getLine(0); maxLineChanged = true;
870
+ var maxLineLength = maxLine.text.length;
871
+ doc.iter(1, doc.size, function(line) {
907
872
  var l = line.text;
908
873
  if (!line.hidden && l.length > maxLineLength) {
909
- maxLineLength = l.length; maxLine = l;
874
+ maxLineLength = l.length; maxLine = line;
910
875
  }
911
876
  });
912
877
  updateMaxLine = false;
@@ -922,7 +887,7 @@ var CodeMirror = (function() {
922
887
  var line = pos.line + code.length - (to.line - from.line) - 1;
923
888
  var ch = pos.ch;
924
889
  if (pos.line == to.line)
925
- ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
890
+ ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0));
926
891
  return {line: line, ch: ch};
927
892
  }
928
893
  var end;
@@ -940,7 +905,7 @@ var CodeMirror = (function() {
940
905
  });
941
906
  }
942
907
  function replaceRange1(code, from, to, computeSel) {
943
- var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
908
+ var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length;
944
909
  var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
945
910
  updateLines(from, to, code, newSel.from, newSel.to);
946
911
  }
@@ -957,25 +922,20 @@ var CodeMirror = (function() {
957
922
  return getRange(sel.from, sel.to, lineSep);
958
923
  }
959
924
 
960
- var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
961
925
  function slowPoll() {
962
926
  if (pollingFast) return;
963
927
  poll.set(options.pollInterval, function() {
964
- startOperation();
965
928
  readInput();
966
929
  if (focused) slowPoll();
967
- endOperation();
968
930
  });
969
931
  }
970
932
  function fastPoll() {
971
933
  var missed = false;
972
934
  pollingFast = true;
973
935
  function p() {
974
- startOperation();
975
936
  var changed = readInput();
976
937
  if (!changed && !missed) {missed = true; poll.set(60, p);}
977
938
  else {pollingFast = false; slowPoll();}
978
- endOperation();
979
939
  }
980
940
  poll.set(20, p);
981
941
  }
@@ -987,9 +947,10 @@ var CodeMirror = (function() {
987
947
  // supported or compatible enough yet to rely on.)
988
948
  var prevInput = "";
989
949
  function readInput() {
990
- if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
950
+ if (!focused || hasSelection(input) || options.readOnly) return false;
991
951
  var text = input.value;
992
952
  if (text == prevInput) return false;
953
+ if (!nestedOperation) startOperation();
993
954
  shiftSelecting = null;
994
955
  var same = 0, l = Math.min(prevInput.length, text.length);
995
956
  while (same < l && prevInput[same] == text[same]) ++same;
@@ -1000,13 +961,14 @@ var CodeMirror = (function() {
1000
961
  replaceSelection(text.slice(same), "end");
1001
962
  if (text.length > 1000) { input.value = prevInput = ""; }
1002
963
  else prevInput = text;
964
+ if (!nestedOperation) endOperation();
1003
965
  return true;
1004
966
  }
1005
967
  function resetInput(user) {
1006
968
  if (!posEq(sel.from, sel.to)) {
1007
969
  prevInput = "";
1008
970
  input.value = getSelection();
1009
- selectInput(input);
971
+ if (focused) selectInput(input);
1010
972
  } else if (user) prevInput = input.value = "";
1011
973
  }
1012
974
 
@@ -1014,16 +976,23 @@ var CodeMirror = (function() {
1014
976
  if (options.readOnly != "nocursor") input.focus();
1015
977
  }
1016
978
 
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
979
  function scrollCursorIntoView() {
1025
980
  var coords = calculateCursorCoords();
1026
- return scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
981
+ scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
982
+ if (!focused) return;
983
+ var box = sizer.getBoundingClientRect(), doScroll = null;
984
+ if (coords.y + box.top < 0) doScroll = true;
985
+ else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
986
+ if (doScroll != null) {
987
+ var hidden = cursor.style.display == "none";
988
+ if (hidden) {
989
+ cursor.style.display = "";
990
+ cursor.style.left = coords.x + "px";
991
+ cursor.style.top = (coords.y - displayOffset) + "px";
992
+ }
993
+ cursor.scrollIntoView(doScroll);
994
+ if (hidden) cursor.style.display = "none";
995
+ }
1027
996
  }
1028
997
  function calculateCursorCoords() {
1029
998
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
@@ -1031,17 +1000,16 @@ var CodeMirror = (function() {
1031
1000
  return {x: x, y: cursor.y, yBot: cursor.yBot};
1032
1001
  }
1033
1002
  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);
1003
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2);
1004
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;}
1005
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;}
1038
1006
  }
1039
1007
  function calculateScrollPos(x1, y1, x2, y2) {
1040
1008
  var pl = paddingLeft(), pt = paddingTop();
1041
1009
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
1042
1010
  var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
1043
- var docBottom = scroller.scrollHeight;
1044
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;;
1011
+ var docBottom = needsScrollbar() || Infinity;
1012
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
1045
1013
  if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1046
1014
  else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
1047
1015
 
@@ -1113,8 +1081,13 @@ var CodeMirror = (function() {
1113
1081
  // This is just a bogus formula that detects when the editor is
1114
1082
  // resized or the font size changes.
1115
1083
  if (different) lastSizeC = scroller.clientHeight + th;
1084
+ if (from != showingFrom || to != showingTo && options.onViewportChange)
1085
+ setTimeout(function(){
1086
+ if (options.onViewportChange) options.onViewportChange(instance, from, to);
1087
+ });
1116
1088
  showingFrom = from; showingTo = to;
1117
1089
  displayOffset = heightAtLine(doc, from);
1090
+ startWorker(100);
1118
1091
 
1119
1092
  // Since this is all rather error prone, it is honoured with the
1120
1093
  // only assertion in the whole file.
@@ -1125,6 +1098,10 @@ var CodeMirror = (function() {
1125
1098
  function checkHeights() {
1126
1099
  var curNode = lineDiv.firstChild, heightChanged = false;
1127
1100
  doc.iter(showingFrom, showingTo, function(line) {
1101
+ // Work around bizarro IE7 bug where, sometimes, our curNode
1102
+ // is magically replaced with a new node in the DOM, leaving
1103
+ // us with a reference to an orphan (nextSibling-less) node.
1104
+ if (!curNode) return;
1128
1105
  if (!line.hidden) {
1129
1106
  var height = Math.round(curNode.offsetHeight / th) || 1;
1130
1107
  if (line.height != height) {
@@ -1137,16 +1114,7 @@ var CodeMirror = (function() {
1137
1114
  return heightChanged;
1138
1115
  }
1139
1116
 
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
- }
1117
+ if (options.lineWrapping) checkHeights();
1150
1118
 
1151
1119
  gutter.style.display = gutterDisplay;
1152
1120
  if (different || gutterDirty) {
@@ -1183,14 +1151,14 @@ var CodeMirror = (function() {
1183
1151
  }
1184
1152
 
1185
1153
  function patchDisplay(from, to, intact) {
1154
+ function killNode(node) {
1155
+ var tmp = node.nextSibling;
1156
+ node.parentNode.removeChild(node);
1157
+ return tmp;
1158
+ }
1186
1159
  // The first pass removes the DOM nodes that aren't intact.
1187
- if (!intact.length) lineDiv.innerHTML = "";
1160
+ if (!intact.length) removeChildren(lineDiv);
1188
1161
  else {
1189
- function killNode(node) {
1190
- var tmp = node.nextSibling;
1191
- node.parentNode.removeChild(node);
1192
- return tmp;
1193
- }
1194
1162
  var domPos = 0, curNode = lineDiv.firstChild, n;
1195
1163
  for (var i = 0; i < intact.length; ++i) {
1196
1164
  var cur = intact[i];
@@ -1201,21 +1169,20 @@ var CodeMirror = (function() {
1201
1169
  }
1202
1170
  // This pass fills in the lines that actually changed.
1203
1171
  var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
1204
- var scratch = document.createElement("div");
1205
1172
  doc.iter(from, to, function(line) {
1206
1173
  if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
1207
1174
  if (!nextIntact || nextIntact.from > j) {
1208
- if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
1175
+ if (line.hidden) var lineElement = elt("pre");
1209
1176
  else {
1210
- var html = '<pre' + (line.className ? ' class="' + line.className + '"' : '') + '>'
1211
- + line.getHTML(makeTab) + '</pre>';
1177
+ var lineElement = lineContent(line);
1178
+ if (line.className) lineElement.className = line.className;
1212
1179
  // 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>";
1180
+ if (line.bgClassName) {
1181
+ var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2");
1182
+ lineElement = elt("div", [pre, lineElement], null, "position: relative");
1183
+ }
1216
1184
  }
1217
- scratch.innerHTML = html;
1218
- lineDiv.insertBefore(scratch.firstChild, curNode);
1185
+ lineDiv.insertBefore(lineElement, curNode);
1219
1186
  } else {
1220
1187
  curNode = curNode.nextSibling;
1221
1188
  }
@@ -1227,10 +1194,10 @@ var CodeMirror = (function() {
1227
1194
  if (!options.gutter && !options.lineNumbers) return;
1228
1195
  var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
1229
1196
  gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
1230
- var html = [], i = showingFrom, normalNode;
1197
+ var fragment = document.createDocumentFragment(), i = showingFrom, normalNode;
1231
1198
  doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
1232
1199
  if (line.hidden) {
1233
- html.push("<pre></pre>");
1200
+ fragment.appendChild(elt("pre"));
1234
1201
  } else {
1235
1202
  var marker = line.gutterMarker;
1236
1203
  var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
@@ -1238,15 +1205,18 @@ var CodeMirror = (function() {
1238
1205
  text = marker.text.replace("%N%", text != null ? text : "");
1239
1206
  else if (text == null)
1240
1207
  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>");
1208
+ var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style));
1209
+ markerElement.innerHTML = text;
1210
+ for (var j = 1; j < line.height; ++j) {
1211
+ markerElement.appendChild(elt("br"));
1212
+ markerElement.appendChild(document.createTextNode("\u00a0"));
1213
+ }
1244
1214
  if (!marker) normalNode = i;
1245
1215
  }
1246
1216
  ++i;
1247
1217
  });
1248
1218
  gutter.style.display = "none";
1249
- gutterText.innerHTML = html.join("");
1219
+ removeChildrenAndAdd(gutterText, fragment);
1250
1220
  // Make sure scrolling doesn't cause number gutter size to pop
1251
1221
  if (normalNode != null && options.lineNumbers) {
1252
1222
  var node = gutterText.childNodes[normalNode - showingFrom];
@@ -1274,15 +1244,15 @@ var CodeMirror = (function() {
1274
1244
  cursor.style.display = "";
1275
1245
  selectionDiv.style.display = "none";
1276
1246
  } else {
1277
- var sameLine = fromPos.y == toPos.y, html = "";
1247
+ var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment();
1278
1248
  var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1279
1249
  var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1280
- function add(left, top, right, height) {
1250
+ var add = function(left, top, right, height) {
1281
1251
  var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
1282
1252
  : "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
- }
1253
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
1254
+ "px; top: " + top + "px; " + rstyle + "; height: " + height + "px"));
1255
+ };
1286
1256
  if (sel.from.ch && fromPos.y >= 0) {
1287
1257
  var right = sameLine ? clientWidth - toPos.x : 0;
1288
1258
  add(fromPos.x, fromPos.y, right, th);
@@ -1293,7 +1263,7 @@ var CodeMirror = (function() {
1293
1263
  add(0, middleStart, 0, middleHeight);
1294
1264
  if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
1295
1265
  add(0, toPos.y, clientWidth - toPos.x, th);
1296
- selectionDiv.innerHTML = html;
1266
+ removeChildrenAndAdd(selectionDiv, fragment);
1297
1267
  cursor.style.display = "none";
1298
1268
  selectionDiv.style.display = "";
1299
1269
  }
@@ -1425,13 +1395,16 @@ var CodeMirror = (function() {
1425
1395
  else replaceRange("", sel.from, findPosH(dir, unit));
1426
1396
  userSelChange = true;
1427
1397
  }
1428
- var goalColumn = null;
1429
1398
  function moveV(dir, unit) {
1430
1399
  var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1431
1400
  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);
1401
+ if (unit == "page") {
1402
+ var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1403
+ var target = coordsChar(pos.x, pos.y + screen * dir);
1404
+ } else if (unit == "line") {
1405
+ var th = textHeight();
1406
+ var target = coordsChar(pos.x, pos.y + .5 * th + dir * th);
1407
+ }
1435
1408
  if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
1436
1409
  setCursor(target.line, target.ch, true);
1437
1410
  goalColumn = pos.x;
@@ -1440,10 +1413,15 @@ var CodeMirror = (function() {
1440
1413
  function findWordAt(pos) {
1441
1414
  var line = getLine(pos.line).text;
1442
1415
  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;
1416
+ if (line) {
1417
+ if (pos.after === false || end == line.length) --start; else ++end;
1418
+ var startChar = line.charAt(start);
1419
+ var check = isWordChar(startChar) ? isWordChar :
1420
+ /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
1421
+ function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
1422
+ while (start > 0 && check(line.charAt(start - 1))) --start;
1423
+ while (end < line.length && check(line.charAt(end))) ++end;
1424
+ }
1447
1425
  return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
1448
1426
  }
1449
1427
  function selectLine(line) {
@@ -1480,16 +1458,17 @@ var CodeMirror = (function() {
1480
1458
  var indentString = "", pos = 0;
1481
1459
  if (options.indentWithTabs)
1482
1460
  for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1483
- while (pos < indentation) {++pos; indentString += " ";}
1461
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
1484
1462
 
1485
- replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1463
+ if (indentString != curSpaceString)
1464
+ replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1486
1465
  }
1487
1466
 
1488
1467
  function loadMode() {
1489
1468
  mode = CodeMirror.getMode(options, options.mode);
1490
1469
  doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1491
- work = [0];
1492
- startWorker();
1470
+ frontier = 0;
1471
+ startWorker(100);
1493
1472
  }
1494
1473
  function gutterChanged() {
1495
1474
  var visible = options.gutter || options.lineNumbers;
@@ -1506,24 +1485,16 @@ var CodeMirror = (function() {
1506
1485
  var guess = Math.ceil(line.text.length / perLine) || 1;
1507
1486
  if (guess != 1) updateLineHeight(line, guess);
1508
1487
  });
1509
- lineSpace.style.width = code.style.width = "";
1510
- widthForcer.style.left = "";
1488
+ lineSpace.style.minWidth = widthForcer.style.left = "";
1511
1489
  } else {
1512
1490
  wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1513
- maxLine = ""; maxLineChanged = true;
1491
+ computeMaxLength();
1514
1492
  doc.iter(0, doc.size, function(line) {
1515
1493
  if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1516
- if (line.text.length > maxLine.length) maxLine = line.text;
1517
1494
  });
1518
1495
  }
1519
1496
  changes.push({from: 0, to: doc.size});
1520
1497
  }
1521
- function makeTab(col) {
1522
- var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
1523
- 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});
1526
- }
1527
1498
  function themeChanged() {
1528
1499
  scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1529
1500
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
@@ -1534,74 +1505,71 @@ var CodeMirror = (function() {
1534
1505
  (style ? " cm-keymap-" + style : "");
1535
1506
  }
1536
1507
 
1537
- function TextMarker() { this.set = []; }
1508
+ function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; }
1538
1509
  TextMarker.prototype.clear = operation(function() {
1539
1510
  var min = Infinity, max = -Infinity;
1540
- for (var i = 0, e = this.set.length; i < e; ++i) {
1541
- var line = this.set[i], mk = line.marked;
1542
- if (!mk || !line.parent) continue;
1543
- var lineN = lineNo(line);
1544
- min = Math.min(min, lineN); max = Math.max(max, lineN);
1545
- for (var j = 0; j < mk.length; ++j)
1546
- if (mk[j].marker == this) mk.splice(j--, 1);
1511
+ for (var i = 0; i < this.lines.length; ++i) {
1512
+ var line = this.lines[i];
1513
+ var span = getMarkedSpanFor(line.markedSpans, this, true);
1514
+ if (span.from != null || span.to != null) {
1515
+ var lineN = lineNo(line);
1516
+ min = Math.min(min, lineN); max = Math.max(max, lineN);
1517
+ }
1547
1518
  }
1548
1519
  if (min != Infinity)
1549
1520
  changes.push({from: min, to: max + 1});
1521
+ this.lines.length = 0;
1550
1522
  });
1551
1523
  TextMarker.prototype.find = function() {
1552
1524
  var from, to;
1553
- for (var i = 0, e = this.set.length; i < e; ++i) {
1554
- var line = this.set[i], mk = line.marked;
1555
- for (var j = 0; j < mk.length; ++j) {
1556
- var mark = mk[j];
1557
- if (mark.marker == this) {
1558
- if (mark.from != null || mark.to != null) {
1559
- var found = lineNo(line);
1560
- if (found != null) {
1561
- if (mark.from != null) from = {line: found, ch: mark.from};
1562
- if (mark.to != null) to = {line: found, ch: mark.to};
1563
- }
1564
- }
1565
- }
1525
+ for (var i = 0; i < this.lines.length; ++i) {
1526
+ var line = this.lines[i];
1527
+ var span = getMarkedSpanFor(line.markedSpans, this);
1528
+ if (span.from != null || span.to != null) {
1529
+ var found = lineNo(line);
1530
+ if (span.from != null) from = {line: found, ch: span.from};
1531
+ if (span.to != null) to = {line: found, ch: span.to};
1566
1532
  }
1567
1533
  }
1568
- return {from: from, to: to};
1534
+ if (this.type == "bookmark") return from;
1535
+ return from && {from: from, to: to};
1569
1536
  };
1570
1537
 
1571
- function markText(from, to, className) {
1538
+ function markText(from, to, className, options) {
1572
1539
  from = clipPos(from); to = clipPos(to);
1573
- var tm = new TextMarker();
1574
- if (!posLess(from, to)) return tm;
1575
- function add(line, from, to, className) {
1576
- getLine(line).addMark(new MarkedText(from, to, className, tm));
1577
- }
1578
- if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1579
- else {
1580
- add(from.line, from.ch, null, className);
1581
- for (var i = from.line + 1, e = to.line; i < e; ++i)
1582
- add(i, null, null, className);
1583
- add(to.line, null, to.ch, className);
1584
- }
1540
+ var marker = new TextMarker("range", className);
1541
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
1542
+ marker[opt] = options[opt];
1543
+ var curLine = from.line;
1544
+ doc.iter(curLine, to.line + 1, function(line) {
1545
+ var span = {from: curLine == from.line ? from.ch : null,
1546
+ to: curLine == to.line ? to.ch : null,
1547
+ marker: marker};
1548
+ (line.markedSpans || (line.markedSpans = [])).push(span);
1549
+ marker.lines.push(line);
1550
+ ++curLine;
1551
+ });
1585
1552
  changes.push({from: from.line, to: to.line + 1});
1586
- return tm;
1553
+ return marker;
1587
1554
  }
1588
1555
 
1589
1556
  function setBookmark(pos) {
1590
1557
  pos = clipPos(pos);
1591
- var bm = new Bookmark(pos.ch);
1592
- getLine(pos.line).addMark(bm);
1593
- return bm;
1558
+ var marker = new TextMarker("bookmark"), line = getLine(pos.line);
1559
+ var span = {from: pos.ch, to: pos.ch, marker: marker};
1560
+ (line.markedSpans || (line.markedSpans = [])).push(span);
1561
+ marker.lines.push(line);
1562
+ return marker;
1594
1563
  }
1595
1564
 
1596
1565
  function findMarksAt(pos) {
1597
1566
  pos = clipPos(pos);
1598
- var markers = [], marked = getLine(pos.line).marked;
1599
- if (!marked) return markers;
1600
- for (var i = 0, e = marked.length; i < e; ++i) {
1601
- var m = marked[i];
1602
- if ((m.from == null || m.from <= pos.ch) &&
1603
- (m.to == null || m.to >= pos.ch))
1604
- markers.push(m.marker || m);
1567
+ var markers = [], spans = getLine(pos.line).markedSpans;
1568
+ if (spans) for (var i = 0; i < spans.length; ++i) {
1569
+ var span = spans[i];
1570
+ if ((span.from == null || span.from <= pos.ch) &&
1571
+ (span.to == null || span.to >= pos.ch))
1572
+ markers.push(span.marker);
1605
1573
  }
1606
1574
  return markers;
1607
1575
  }
@@ -1641,11 +1609,10 @@ var CodeMirror = (function() {
1641
1609
  if (line.hidden != hidden) {
1642
1610
  line.hidden = hidden;
1643
1611
  if (!options.lineWrapping) {
1644
- var l = line.text;
1645
- if (hidden && l.length == maxLine.length) {
1612
+ if (hidden && line.text.length == maxLine.text.length) {
1646
1613
  updateMaxLine = true;
1647
- } else if (!hidden && l.length > maxLine.length) {
1648
- maxLine = l; updateMaxLine = false;
1614
+ } else if (!hidden && line.text.length > maxLine.text.length) {
1615
+ maxLine = line; updateMaxLine = false;
1649
1616
  }
1650
1617
  }
1651
1618
  updateLineHeight(line, hidden ? 0 : 1);
@@ -1677,53 +1644,18 @@ var CodeMirror = (function() {
1677
1644
  markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1678
1645
  }
1679
1646
 
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
- // These are used to go from pixel positions to character
1686
- // positions, taking varying character widths into account.
1687
- function charFromX(line, x) {
1688
- if (x <= 0) return 0;
1689
- var lineObj = getLine(line), text = lineObj.text;
1690
- function getX(len) {
1691
- return measureLine(lineObj, len).left;
1692
- }
1693
- var from = 0, fromX = 0, to = text.length, toX;
1694
- // Guess a suitable upper bound for our search.
1695
- var estimated = Math.min(to, Math.ceil(x / charWidth()));
1696
- for (;;) {
1697
- var estX = getX(estimated);
1698
- if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1699
- else {toX = estX; to = estimated; break;}
1700
- }
1701
- if (x > toX) return to;
1702
- // Try to guess a suitable lower bound as well.
1703
- estimated = Math.floor(to * 0.8); estX = getX(estimated);
1704
- if (estX < x) {from = estimated; fromX = estX;}
1705
- // Do a binary search between these bounds.
1706
- for (;;) {
1707
- if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1708
- var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1709
- if (middleX > x) {to = middle; toX = middleX;}
1710
- else {from = middle; fromX = middleX;}
1711
- }
1712
- }
1713
-
1714
- var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16);
1715
1647
  function measureLine(line, ch) {
1716
1648
  if (ch == 0) return {top: 0, left: 0};
1717
1649
  var wbr = options.lineWrapping && ch < line.text.length &&
1718
1650
  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;
1651
+ var pre = lineContent(line, ch);
1652
+ removeChildrenAndAdd(measure, pre);
1653
+ var anchor = pre.anchor;
1654
+ var top = anchor.offsetTop, left = anchor.offsetLeft;
1722
1655
  // Older IEs report zero offsets for spans directly after a wrap
1723
1656
  if (ie && top == 0 && left == 0) {
1724
- var backup = document.createElement("span");
1725
- backup.innerHTML = "x";
1726
- elt.parentNode.insertBefore(backup, elt.nextSibling);
1657
+ var backup = elt("span", "x");
1658
+ anchor.parentNode.insertBefore(backup, anchor.nextSibling);
1727
1659
  top = backup.offsetTop;
1728
1660
  }
1729
1661
  return {top: top, left: left};
@@ -1740,17 +1672,19 @@ var CodeMirror = (function() {
1740
1672
  }
1741
1673
  // Coords must be lineSpace-local
1742
1674
  function coordsChar(x, y) {
1743
- if (y < 0) y = 0;
1744
1675
  var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1676
+ if (heightPos < 0) return {line: 0, ch: 0};
1745
1677
  var lineNo = lineAtHeight(doc, heightPos);
1746
1678
  if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1747
1679
  var lineObj = getLine(lineNo), text = lineObj.text;
1748
1680
  var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1749
1681
  if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1682
+ var wrongLine = false;
1750
1683
  function getX(len) {
1751
1684
  var sp = measureLine(lineObj, len);
1752
1685
  if (tw) {
1753
1686
  var off = Math.round(sp.top / th);
1687
+ wrongLine = off != innerOff;
1754
1688
  return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1755
1689
  }
1756
1690
  return sp.left;
@@ -1769,9 +1703,12 @@ var CodeMirror = (function() {
1769
1703
  if (estX < x) {from = estimated; fromX = estX;}
1770
1704
  // Do a binary search between these bounds.
1771
1705
  for (;;) {
1772
- if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
1706
+ if (to - from <= 1) {
1707
+ var after = x - fromX < toX - x;
1708
+ return {line: lineNo, ch: after ? from : to, after: after};
1709
+ }
1773
1710
  var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1774
- if (middleX > x) {to = middle; toX = middleX;}
1711
+ if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; }
1775
1712
  else {from = middle; fromX = middleX;}
1776
1713
  }
1777
1714
  }
@@ -1780,26 +1717,32 @@ var CodeMirror = (function() {
1780
1717
  return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1781
1718
  }
1782
1719
 
1783
- var cachedHeight, cachedHeightFor, measureText;
1720
+ var cachedHeight, cachedHeightFor, measurePre;
1784
1721
  function textHeight() {
1785
- if (measureText == null) {
1786
- measureText = "<pre>";
1787
- for (var i = 0; i < 49; ++i) measureText += "x<br/>";
1788
- measureText += "x</pre>";
1722
+ if (measurePre == null) {
1723
+ measurePre = elt("pre");
1724
+ for (var i = 0; i < 49; ++i) {
1725
+ measurePre.appendChild(document.createTextNode("x"));
1726
+ measurePre.appendChild(elt("br"));
1727
+ }
1728
+ measurePre.appendChild(document.createTextNode("x"));
1789
1729
  }
1790
1730
  var offsetHeight = lineDiv.clientHeight;
1791
1731
  if (offsetHeight == cachedHeightFor) return cachedHeight;
1792
1732
  cachedHeightFor = offsetHeight;
1793
- measure.innerHTML = measureText;
1733
+ removeChildrenAndAdd(measure, measurePre.cloneNode(true));
1794
1734
  cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1795
- measure.innerHTML = "";
1735
+ removeChildren(measure);
1796
1736
  return cachedHeight;
1797
1737
  }
1798
1738
  var cachedWidth, cachedWidthFor = 0;
1799
1739
  function charWidth() {
1800
1740
  if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1801
1741
  cachedWidthFor = scroller.clientWidth;
1802
- return (cachedWidth = stringWidth("x"));
1742
+ var anchor = elt("span", "x");
1743
+ var pre = elt("pre", [anchor]);
1744
+ removeChildrenAndAdd(measure, pre);
1745
+ return (cachedWidth = anchor.offsetWidth || 10);
1803
1746
  }
1804
1747
  function paddingTop() {return lineSpace.offsetTop;}
1805
1748
  function paddingLeft() {return lineSpace.offsetLeft;}
@@ -1816,6 +1759,7 @@ var CodeMirror = (function() {
1816
1759
  var offL = eltOffset(lineSpace, true);
1817
1760
  return coordsChar(x - offL.left, y - offL.top);
1818
1761
  }
1762
+ var detectingSelectAll;
1819
1763
  function onContextMenu(e) {
1820
1764
  var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
1821
1765
  if (!pos || opera) return; // Opera is difficult.
@@ -1827,19 +1771,30 @@ var CodeMirror = (function() {
1827
1771
  input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1828
1772
  "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1829
1773
  "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1830
- leaveInputAlone = true;
1831
- var val = input.value = getSelection();
1832
1774
  focusInput();
1833
- selectInput(input);
1775
+ resetInput(true);
1776
+ // Adds "Select all" to context menu in FF
1777
+ if (posEq(sel.from, sel.to)) input.value = prevInput = " ";
1778
+
1834
1779
  function rehide() {
1835
- var newVal = splitLines(input.value).join("\n");
1836
- if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
1837
1780
  inputDiv.style.position = "relative";
1838
1781
  input.style.cssText = oldCSS;
1839
1782
  if (ie_lt9) scrollbar.scrollTop = scrollPos;
1840
- leaveInputAlone = false;
1841
- resetInput(true);
1842
1783
  slowPoll();
1784
+
1785
+ // Try to detect the user choosing select-all
1786
+ if (input.selectionStart != null) {
1787
+ clearTimeout(detectingSelectAll);
1788
+ var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0;
1789
+ prevInput = " ";
1790
+ input.selectionStart = 1; input.selectionEnd = extval.length;
1791
+ detectingSelectAll = setTimeout(function poll(){
1792
+ if (prevInput == " " && input.selectionStart == 0)
1793
+ operation(commands.selectAll)(instance);
1794
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1795
+ else resetInput();
1796
+ }, 200);
1797
+ }
1843
1798
  }
1844
1799
 
1845
1800
  if (gecko) {
@@ -1860,7 +1815,7 @@ var CodeMirror = (function() {
1860
1815
  cursor.style.visibility = "";
1861
1816
  blinker = setInterval(function() {
1862
1817
  cursor.style.visibility = (on = !on) ? "" : "hidden";
1863
- }, 650);
1818
+ }, options.cursorBlinkRate);
1864
1819
  }
1865
1820
 
1866
1821
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
@@ -1923,70 +1878,39 @@ var CodeMirror = (function() {
1923
1878
  return minline;
1924
1879
  }
1925
1880
  function getStateBefore(n) {
1926
- var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
1881
+ var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter;
1927
1882
  if (!state) state = startState(mode);
1928
1883
  else state = copyState(mode, state);
1929
- doc.iter(start, n, function(line) {
1930
- line.highlight(mode, state, options.tabSize);
1931
- line.stateAfter = copyState(mode, state);
1884
+ doc.iter(pos, n, function(line) {
1885
+ line.process(mode, state, options.tabSize);
1886
+ line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null;
1932
1887
  });
1933
- if (start < n) changes.push({from: start, to: n});
1934
- if (n < doc.size && !getLine(n).stateAfter) work.push(n);
1935
1888
  return state;
1936
1889
  }
1937
- function highlightLines(start, end) {
1938
- var state = getStateBefore(start);
1939
- doc.iter(start, end, function(line) {
1940
- line.highlight(mode, state, options.tabSize);
1941
- line.stateAfter = copyState(mode, state);
1942
- });
1943
- }
1944
1890
  function highlightWorker() {
1945
- var end = +new Date + options.workTime;
1946
- var foundWork = work.length;
1947
- while (work.length) {
1948
- if (!getLine(showingFrom).stateAfter) var task = showingFrom;
1949
- else var task = work.pop();
1950
- if (task >= doc.size) continue;
1951
- var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
1952
- if (state) state = copyState(mode, state);
1953
- else state = startState(mode);
1954
-
1955
- var unchanged = 0, compare = mode.compareStates, realChange = false,
1956
- i = start, bail = false;
1957
- doc.iter(i, doc.size, function(line) {
1958
- var hadState = line.stateAfter;
1959
- if (+new Date > end) {
1960
- work.push(i);
1961
- startWorker(options.workDelay);
1962
- if (realChange) changes.push({from: task, to: i + 1});
1963
- return (bail = true);
1964
- }
1965
- var changed = line.highlight(mode, state, options.tabSize);
1966
- if (changed) realChange = true;
1891
+ if (frontier >= showingTo) return;
1892
+ var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier));
1893
+ var startFrontier = frontier;
1894
+ doc.iter(frontier, showingTo, function(line) {
1895
+ if (frontier >= showingFrom) { // Visible
1896
+ line.highlight(mode, state, options.tabSize);
1967
1897
  line.stateAfter = copyState(mode, state);
1968
- var done = null;
1969
- if (compare) {
1970
- var same = hadState && compare(hadState, state);
1971
- if (same != Pass) done = !!same;
1972
- }
1973
- if (done == null) {
1974
- if (changed !== false || !hadState) unchanged = 0;
1975
- else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1976
- done = true;
1977
- }
1978
- if (done) return true;
1979
- ++i;
1980
- });
1981
- if (bail) return;
1982
- if (realChange) changes.push({from: task, to: i + 1});
1983
- }
1984
- if (foundWork && options.onHighlightComplete)
1985
- options.onHighlightComplete(instance);
1898
+ } else {
1899
+ line.process(mode, state, options.tabSize);
1900
+ line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null;
1901
+ }
1902
+ ++frontier;
1903
+ if (+new Date > end) {
1904
+ startWorker(options.workDelay);
1905
+ return true;
1906
+ }
1907
+ });
1908
+ if (showingTo > startFrontier && frontier >= showingFrom)
1909
+ operation(function() {changes.push({from: startFrontier, to: frontier});})();
1986
1910
  }
1987
1911
  function startWorker(time) {
1988
- if (!work.length) return;
1989
- highlight.set(time, operation(highlightWorker));
1912
+ if (frontier < showingTo)
1913
+ highlight.set(time, highlightWorker);
1990
1914
  }
1991
1915
 
1992
1916
  // Operations are used to wrap changes in such a way that each
@@ -2000,9 +1924,11 @@ var CodeMirror = (function() {
2000
1924
  function endOperation() {
2001
1925
  if (updateMaxLine) computeMaxLength();
2002
1926
  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";
1927
+ var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left;
1928
+ if (!ie_lt8) {
1929
+ widthForcer.style.left = left + "px";
1930
+ lineSpace.style.minWidth = (left + cursorWidth) + "px";
1931
+ }
2006
1932
  maxLineChanged = false;
2007
1933
  }
2008
1934
  var newScrollPos, updated;
@@ -2010,16 +1936,16 @@ var CodeMirror = (function() {
2010
1936
  var coords = calculateCursorCoords();
2011
1937
  newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
2012
1938
  }
2013
- if (changes.length) updated = updateDisplay(changes, true, (newScrollPos ? newScrollPos.scrollTop : null));
2014
- else {
1939
+ if (changes.length || newScrollPos && newScrollPos.scrollTop != null)
1940
+ updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop);
1941
+ if (!updated) {
2015
1942
  if (selectionChanged) updateSelection();
2016
1943
  if (gutterDirty) updateGutter();
2017
1944
  }
2018
1945
  if (newScrollPos) scrollCursorIntoView();
2019
- if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1946
+ if (selectionChanged) restartBlink();
2020
1947
 
2021
- if (focused && !leaveInputAlone &&
2022
- (updateInput === true || (updateInput !== false && selectionChanged)))
1948
+ if (focused && (updateInput === true || (updateInput !== false && selectionChanged)))
2023
1949
  resetInput(userSelChange);
2024
1950
 
2025
1951
  if (selectionChanged && options.matchBrackets)
@@ -2081,11 +2007,12 @@ var CodeMirror = (function() {
2081
2007
  dragDrop: true,
2082
2008
  onChange: null,
2083
2009
  onCursorActivity: null,
2010
+ onViewportChange: null,
2084
2011
  onGutterClick: null,
2085
- onHighlightComplete: null,
2086
2012
  onUpdate: null,
2087
2013
  onFocus: null, onBlur: null, onScroll: null,
2088
2014
  matchBrackets: false,
2015
+ cursorBlinkRate: 530,
2089
2016
  workTime: 100,
2090
2017
  workDelay: 200,
2091
2018
  pollInterval: 100,
@@ -2124,7 +2051,13 @@ var CodeMirror = (function() {
2124
2051
  var spec = CodeMirror.resolveMode(spec);
2125
2052
  var mfactory = modes[spec.name];
2126
2053
  if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2127
- return mfactory(options, spec);
2054
+ var modeObj = mfactory(options, spec);
2055
+ if (modeExtensions.hasOwnProperty(spec.name)) {
2056
+ var exts = modeExtensions[spec.name];
2057
+ for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop];
2058
+ }
2059
+ modeObj.name = spec.name;
2060
+ return modeObj;
2128
2061
  };
2129
2062
  CodeMirror.listModes = function() {
2130
2063
  var list = [];
@@ -2144,6 +2077,13 @@ var CodeMirror = (function() {
2144
2077
  extensions[name] = func;
2145
2078
  };
2146
2079
 
2080
+ var modeExtensions = CodeMirror.modeExtensions = {};
2081
+ CodeMirror.extendMode = function(mode, properties) {
2082
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2083
+ for (var prop in properties) if (properties.hasOwnProperty(prop))
2084
+ exts[prop] = properties[prop];
2085
+ };
2086
+
2147
2087
  var commands = CodeMirror.commands = {
2148
2088
  selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
2149
2089
  killLine: function(cm) {
@@ -2241,6 +2181,10 @@ var CodeMirror = (function() {
2241
2181
  function lookup(map) {
2242
2182
  map = getKeyMap(map);
2243
2183
  var found = map[name];
2184
+ if (found === false) {
2185
+ if (stop) stop();
2186
+ return true;
2187
+ }
2244
2188
  if (found != null && handle(found)) return true;
2245
2189
  if (map.nofallthrough) {
2246
2190
  if (stop) stop();
@@ -2268,8 +2212,15 @@ var CodeMirror = (function() {
2268
2212
  options.value = textarea.value;
2269
2213
  if (!options.tabindex && textarea.tabindex)
2270
2214
  options.tabindex = textarea.tabindex;
2271
- if (options.autofocus == null && textarea.getAttribute("autofocus") != null)
2272
- options.autofocus = true;
2215
+ // Set autofocus to true if this textarea is focused, or if it has
2216
+ // autofocus and no other element is focused.
2217
+ if (options.autofocus == null) {
2218
+ var hasFocus = document.body;
2219
+ // doc.activeElement occasionally throws on IE
2220
+ try { hasFocus = document.activeElement; } catch(e) {}
2221
+ options.autofocus = hasFocus == textarea ||
2222
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
2223
+ }
2273
2224
 
2274
2225
  function save() {textarea.value = instance.getValue();}
2275
2226
  if (textarea.form) {
@@ -2277,13 +2228,12 @@ var CodeMirror = (function() {
2277
2228
  var rmSubmit = connect(textarea.form, "submit", save, true);
2278
2229
  if (typeof textarea.form.submit == "function") {
2279
2230
  var realSubmit = textarea.form.submit;
2280
- function wrappedSubmit() {
2231
+ textarea.form.submit = function wrappedSubmit() {
2281
2232
  save();
2282
2233
  textarea.form.submit = realSubmit;
2283
2234
  textarea.form.submit();
2284
2235
  textarea.form.submit = wrappedSubmit;
2285
- }
2286
- textarea.form.submit = wrappedSubmit;
2236
+ };
2287
2237
  }
2288
2238
  }
2289
2239
 
@@ -2306,6 +2256,18 @@ var CodeMirror = (function() {
2306
2256
  return instance;
2307
2257
  };
2308
2258
 
2259
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2260
+ var ie = /MSIE \d/.test(navigator.userAgent);
2261
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
2262
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2263
+ var quirksMode = ie && document.documentMode == 5;
2264
+ var webkit = /WebKit\//.test(navigator.userAgent);
2265
+ var chrome = /Chrome\//.test(navigator.userAgent);
2266
+ var opera = /Opera\//.test(navigator.userAgent);
2267
+ var safari = /Apple Computer/.test(navigator.vendor);
2268
+ var khtml = /KHTML\//.test(navigator.userAgent);
2269
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
2270
+
2309
2271
  // Utility functions for working with state. Exported because modes
2310
2272
  // sometimes need to do this.
2311
2273
  function copyState(mode, state) {
@@ -2324,6 +2286,14 @@ var CodeMirror = (function() {
2324
2286
  return mode.startState ? mode.startState(a1, a2) : true;
2325
2287
  }
2326
2288
  CodeMirror.startState = startState;
2289
+ CodeMirror.innerMode = function(mode, state) {
2290
+ while (mode.innerMode) {
2291
+ var info = mode.innerMode(state);
2292
+ state = info.state;
2293
+ mode = info.mode;
2294
+ }
2295
+ return info || {mode: mode, state: state};
2296
+ };
2327
2297
 
2328
2298
  // The character stream used by a mode's parser.
2329
2299
  function StringStream(string, tabSize) {
@@ -2334,7 +2304,7 @@ var CodeMirror = (function() {
2334
2304
  StringStream.prototype = {
2335
2305
  eol: function() {return this.pos >= this.string.length;},
2336
2306
  sol: function() {return this.pos == 0;},
2337
- peek: function() {return this.string.charAt(this.pos);},
2307
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
2338
2308
  next: function() {
2339
2309
  if (this.pos < this.string.length)
2340
2310
  return this.string.charAt(this.pos++);
@@ -2365,13 +2335,14 @@ var CodeMirror = (function() {
2365
2335
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
2366
2336
  match: function(pattern, consume, caseInsensitive) {
2367
2337
  if (typeof pattern == "string") {
2368
- function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
2338
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
2369
2339
  if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
2370
2340
  if (consume !== false) this.pos += pattern.length;
2371
2341
  return true;
2372
2342
  }
2373
2343
  } else {
2374
2344
  var match = this.string.slice(this.pos).match(pattern);
2345
+ if (match && match.index > 0) return null;
2375
2346
  if (match && consume !== false) this.pos += match[0].length;
2376
2347
  return match;
2377
2348
  }
@@ -2380,210 +2351,162 @@ var CodeMirror = (function() {
2380
2351
  };
2381
2352
  CodeMirror.StringStream = StringStream;
2382
2353
 
2383
- function MarkedText(from, to, className, marker) {
2384
- this.from = from; this.to = to; this.style = className; this.marker = marker;
2354
+ function MarkedSpan(from, to, marker) {
2355
+ this.from = from; this.to = to; this.marker = marker;
2385
2356
  }
2386
- MarkedText.prototype = {
2387
- attach: function(line) { this.marker.set.push(line); },
2388
- detach: function(line) {
2389
- var ix = indexOf(this.marker.set, line);
2390
- if (ix > -1) this.marker.set.splice(ix, 1);
2391
- },
2392
- split: function(pos, lenBefore) {
2393
- if (this.to <= pos && this.to != null) return null;
2394
- var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2395
- var to = this.to == null ? null : this.to - pos + lenBefore;
2396
- return new MarkedText(from, to, this.style, this.marker);
2397
- },
2398
- dup: function() { return new MarkedText(null, null, this.style, this.marker); },
2399
- clipTo: function(fromOpen, from, toOpen, to, diff) {
2400
- if (fromOpen && to > this.from && (to < this.to || this.to == null))
2401
- this.from = null;
2402
- else if (this.from != null && this.from >= from)
2403
- this.from = Math.max(to, this.from) + diff;
2404
- if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2405
- this.to = null;
2406
- else if (this.to != null && this.to > from)
2407
- this.to = to < this.to ? this.to + diff : from;
2408
- },
2409
- isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2410
- sameSet: function(x) { return this.marker == x.marker; }
2411
- };
2412
2357
 
2413
- function Bookmark(pos) {
2414
- this.from = pos; this.to = pos; this.line = null;
2415
- }
2416
- Bookmark.prototype = {
2417
- attach: function(line) { this.line = line; },
2418
- detach: function(line) { if (this.line == line) this.line = null; },
2419
- split: function(pos, lenBefore) {
2420
- if (pos < this.from) {
2421
- this.from = this.to = (this.from - pos) + lenBefore;
2422
- return this;
2423
- }
2424
- },
2425
- isDead: function() { return this.from > this.to; },
2426
- clipTo: function(fromOpen, from, toOpen, to, diff) {
2427
- if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
2428
- this.from = 0; this.to = -1;
2429
- } else if (this.from > from) {
2430
- this.from = this.to = Math.max(to, this.from) + diff;
2431
- }
2432
- },
2433
- sameSet: function(x) { return false; },
2434
- find: function() {
2435
- if (!this.line || !this.line.parent) return null;
2436
- return {line: lineNo(this.line), ch: this.from};
2437
- },
2438
- clear: function() {
2439
- if (this.line) {
2440
- var found = indexOf(this.line.marked, this);
2441
- if (found != -1) this.line.marked.splice(found, 1);
2442
- this.line = null;
2358
+ function getMarkedSpanFor(spans, marker, del) {
2359
+ if (spans) for (var i = 0; i < spans.length; ++i) {
2360
+ var span = spans[i];
2361
+ if (span.marker == marker) {
2362
+ if (del) spans.splice(i, 1);
2363
+ return span;
2443
2364
  }
2444
2365
  }
2445
- };
2446
-
2447
- // Line objects. These hold state related to a line, including
2448
- // highlighting info (the styles array).
2449
- function Line(text, styles) {
2450
- this.styles = styles || [text, null];
2451
- this.text = text;
2452
- this.height = 1;
2453
- this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null;
2454
- this.stateAfter = this.parent = this.hidden = null;
2455
2366
  }
2456
- Line.inheritMarks = function(text, orig) {
2457
- var ln = new Line(text), mk = orig && orig.marked;
2458
- if (mk) {
2459
- for (var i = 0; i < mk.length; ++i) {
2460
- if (mk[i].to == null && mk[i].style) {
2461
- var newmk = ln.marked || (ln.marked = []), mark = mk[i];
2462
- var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
2463
- }
2367
+
2368
+ function markedSpansBefore(old, startCh, endCh) {
2369
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
2370
+ var span = old[i], marker = span.marker;
2371
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2372
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) {
2373
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
2374
+ (nw || (nw = [])).push({from: span.from,
2375
+ to: endsAfter ? null : span.to,
2376
+ marker: marker});
2464
2377
  }
2465
2378
  }
2466
- return ln;
2379
+ return nw;
2467
2380
  }
2468
- Line.prototype = {
2469
- // Replace a piece of a line, keeping the styles around it intact.
2470
- replace: function(from, to_, text) {
2471
- var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
2472
- copyStyles(0, from, this.styles, st);
2473
- if (text) st.push(text, null);
2474
- copyStyles(to, this.text.length, this.styles, st);
2475
- this.styles = st;
2476
- this.text = this.text.slice(0, from) + text + this.text.slice(to);
2477
- this.stateAfter = null;
2478
- if (mk) {
2479
- var diff = text.length - (to - from);
2480
- for (var i = 0; i < mk.length; ++i) {
2481
- var mark = mk[i];
2482
- mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2483
- if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2484
- }
2485
- }
2486
- },
2487
- // Split a part off a line, keeping styles and markers intact.
2488
- split: function(pos, textBefore) {
2489
- var st = [textBefore, null], mk = this.marked;
2490
- copyStyles(pos, this.text.length, this.styles, st);
2491
- var taken = new Line(textBefore + this.text.slice(pos), st);
2492
- if (mk) {
2493
- for (var i = 0; i < mk.length; ++i) {
2494
- var mark = mk[i];
2495
- var newmark = mark.split(pos, textBefore.length);
2496
- if (newmark) {
2497
- if (!taken.marked) taken.marked = [];
2498
- taken.marked.push(newmark); newmark.attach(taken);
2499
- if (newmark == mark) mk.splice(i--, 1);
2500
- }
2501
- }
2381
+
2382
+ function markedSpansAfter(old, endCh) {
2383
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
2384
+ var span = old[i], marker = span.marker;
2385
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2386
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh) {
2387
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
2388
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
2389
+ to: span.to == null ? null : span.to - endCh,
2390
+ marker: marker});
2502
2391
  }
2503
- return taken;
2504
- },
2505
- append: function(line) {
2506
- var mylen = this.text.length, mk = line.marked, mymk = this.marked;
2507
- this.text += line.text;
2508
- copyStyles(0, line.text.length, line.styles, this.styles);
2509
- if (mymk) {
2510
- for (var i = 0; i < mymk.length; ++i)
2511
- if (mymk[i].to == null) mymk[i].to = mylen;
2512
- }
2513
- if (mk && mk.length) {
2514
- if (!mymk) this.marked = mymk = [];
2515
- outer: for (var i = 0; i < mk.length; ++i) {
2516
- var mark = mk[i];
2517
- if (!mark.from) {
2518
- for (var j = 0; j < mymk.length; ++j) {
2519
- var mymark = mymk[j];
2520
- if (mymark.to == mylen && mymark.sameSet(mark)) {
2521
- mymark.to = mark.to == null ? null : mark.to + mylen;
2522
- if (mymark.isDead()) {
2523
- mymark.detach(this);
2524
- mk.splice(i--, 1);
2525
- }
2526
- continue outer;
2527
- }
2528
- }
2529
- }
2530
- mymk.push(mark);
2531
- mark.attach(this);
2532
- mark.from += mylen;
2533
- if (mark.to != null) mark.to += mylen;
2392
+ }
2393
+ return nw;
2394
+ }
2395
+
2396
+ function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
2397
+ if (!oldFirst && !oldLast) return newText;
2398
+ // Get the spans that 'stick out' on both sides
2399
+ var first = markedSpansBefore(oldFirst, startCh);
2400
+ var last = markedSpansAfter(oldLast, endCh);
2401
+
2402
+ // Next, merge those two ends
2403
+ var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
2404
+ if (first) {
2405
+ // Fix up .to properties of first
2406
+ for (var i = 0; i < first.length; ++i) {
2407
+ var span = first[i];
2408
+ if (span.to == null) {
2409
+ var found = getMarkedSpanFor(last, span.marker);
2410
+ if (!found) span.to = startCh;
2411
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
2534
2412
  }
2535
2413
  }
2536
- },
2537
- fixMarkEnds: function(other) {
2538
- var mk = this.marked, omk = other.marked;
2539
- if (!mk) return;
2540
- outer: for (var i = 0; i < mk.length; ++i) {
2541
- var mark = mk[i], close = mark.to == null;
2542
- if (close && omk) {
2543
- for (var j = 0; j < omk.length; ++j) {
2544
- var om = omk[j];
2545
- if (!om.sameSet(mark) || om.from != null) continue
2546
- if (mark.from == this.text.length && om.to == 0) {
2547
- omk.splice(j, 1);
2548
- mk.splice(i--, 1);
2549
- continue outer;
2550
- } else {
2551
- close = false; break;
2552
- }
2414
+ }
2415
+ if (last) {
2416
+ // Fix up .from in last (or move them into first in case of sameLine)
2417
+ for (var i = 0; i < last.length; ++i) {
2418
+ var span = last[i];
2419
+ if (span.to != null) span.to += offset;
2420
+ if (span.from == null) {
2421
+ var found = getMarkedSpanFor(first, span.marker);
2422
+ if (!found) {
2423
+ span.from = offset;
2424
+ if (sameLine) (first || (first = [])).push(span);
2553
2425
  }
2426
+ } else {
2427
+ span.from += offset;
2428
+ if (sameLine) (first || (first = [])).push(span);
2554
2429
  }
2555
- if (close) mark.to = this.text.length;
2556
2430
  }
2557
- },
2558
- fixMarkStarts: function() {
2559
- var mk = this.marked;
2560
- if (!mk) return;
2561
- for (var i = 0; i < mk.length; ++i)
2562
- if (mk[i].from == null) mk[i].from = 0;
2563
- },
2564
- addMark: function(mark) {
2565
- mark.attach(this);
2566
- if (this.marked == null) this.marked = [];
2567
- this.marked.push(mark);
2568
- this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
2431
+ }
2432
+
2433
+ var newMarkers = [newHL(newText[0], first)];
2434
+ if (!sameLine) {
2435
+ // Fill gap with whole-line-spans
2436
+ var gap = newText.length - 2, gapMarkers;
2437
+ if (gap > 0 && first)
2438
+ for (var i = 0; i < first.length; ++i)
2439
+ if (first[i].to == null)
2440
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
2441
+ for (var i = 0; i < gap; ++i)
2442
+ newMarkers.push(newHL(newText[i+1], gapMarkers));
2443
+ newMarkers.push(newHL(lst(newText), last));
2444
+ }
2445
+ return newMarkers;
2446
+ }
2447
+
2448
+ // hl stands for history-line, a data structure that can be either a
2449
+ // string (line without markers) or a {text, markedSpans} object.
2450
+ function hlText(val) { return typeof val == "string" ? val : val.text; }
2451
+ function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; }
2452
+ function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
2453
+
2454
+ function detachMarkedSpans(line) {
2455
+ var spans = line.markedSpans;
2456
+ if (!spans) return;
2457
+ for (var i = 0; i < spans.length; ++i) {
2458
+ var lines = spans[i].marker.lines;
2459
+ var ix = indexOf(lines, line);
2460
+ lines.splice(ix, 1);
2461
+ }
2462
+ line.markedSpans = null;
2463
+ }
2464
+
2465
+ function attachMarkedSpans(line, spans) {
2466
+ if (!spans) return;
2467
+ for (var i = 0; i < spans.length; ++i)
2468
+ var marker = spans[i].marker.lines.push(line);
2469
+ line.markedSpans = spans;
2470
+ }
2471
+
2472
+ // When measuring the position of the end of a line, different
2473
+ // browsers require different approaches. If an empty span is added,
2474
+ // many browsers report bogus offsets. Of those, some (Webkit,
2475
+ // recent IE) will accept a space without moving the whole span to
2476
+ // the next line when wrapping it, others work with a zero-width
2477
+ // space.
2478
+ var eolSpanContent = " ";
2479
+ if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b";
2480
+ else if (opera) eolSpanContent = "";
2481
+
2482
+ // Line objects. These hold state related to a line, including
2483
+ // highlighting info (the styles array).
2484
+ function Line(text, markedSpans) {
2485
+ this.text = text;
2486
+ this.height = 1;
2487
+ attachMarkedSpans(this, markedSpans);
2488
+ }
2489
+ Line.prototype = {
2490
+ update: function(text, markedSpans) {
2491
+ this.text = text;
2492
+ this.stateAfter = this.styles = null;
2493
+ detachMarkedSpans(this);
2494
+ attachMarkedSpans(this, markedSpans);
2569
2495
  },
2570
2496
  // Run the given mode's parser over a line, update the styles
2571
2497
  // array, which contains alternating fragments of text and CSS
2572
2498
  // classes.
2573
2499
  highlight: function(mode, state, tabSize) {
2574
- var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
2575
- var changed = false, curWord = st[0], prevWord;
2500
+ var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []);
2501
+ var pos = st.length = 0;
2576
2502
  if (this.text == "" && mode.blankLine) mode.blankLine(state);
2577
2503
  while (!stream.eol()) {
2578
- var style = mode.token(stream, state);
2579
- var substr = this.text.slice(stream.start, stream.pos);
2504
+ var style = mode.token(stream, state), substr = stream.current();
2580
2505
  stream.start = stream.pos;
2581
- if (pos && st[pos-1] == style)
2506
+ if (pos && st[pos-1] == style) {
2582
2507
  st[pos-2] += substr;
2583
- else if (substr) {
2584
- if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
2508
+ } else if (substr) {
2585
2509
  st[pos++] = substr; st[pos++] = style;
2586
- prevWord = curWord; curWord = st[pos];
2587
2510
  }
2588
2511
  // Give up when line is ridiculously long
2589
2512
  if (stream.pos > 5000) {
@@ -2591,17 +2514,19 @@ var CodeMirror = (function() {
2591
2514
  break;
2592
2515
  }
2593
2516
  }
2594
- if (st.length != pos) {st.length = pos; changed = true;}
2595
- if (pos && st[pos-2] != prevWord) changed = true;
2596
- // Short lines with simple highlights return null, and are
2597
- // counted as changed by the driver because they are likely to
2598
- // highlight the same way in various contexts.
2599
- return changed || (st.length < 5 && this.text.length < 10 ? null : false);
2517
+ },
2518
+ process: function(mode, state, tabSize) {
2519
+ var stream = new StringStream(this.text, tabSize);
2520
+ if (this.text == "" && mode.blankLine) mode.blankLine(state);
2521
+ while (!stream.eol() && stream.pos <= 5000) {
2522
+ mode.token(stream, state);
2523
+ stream.start = stream.pos;
2524
+ }
2600
2525
  },
2601
2526
  // Fetch the parser token for a given character. Useful for hacks
2602
2527
  // 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);
2528
+ getTokenAt: function(mode, state, tabSize, ch) {
2529
+ var txt = this.text, stream = new StringStream(txt, tabSize);
2605
2530
  while (stream.pos < ch && !stream.eol()) {
2606
2531
  stream.start = stream.pos;
2607
2532
  var style = mode.token(stream, state);
@@ -2615,94 +2540,98 @@ var CodeMirror = (function() {
2615
2540
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2616
2541
  // Produces an HTML fragment for the line, taking selection,
2617
2542
  // marking, and highlighting into account.
2618
- getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) {
2619
- var html = [], first = true, col = 0;
2620
- function span_(text, style) {
2543
+ getContent: function(tabSize, wrapAt, compensateForWrapping) {
2544
+ var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
2545
+ var pre = elt("pre");
2546
+ function span_(html, text, style) {
2621
2547
  if (!text) return;
2622
2548
  // Work around a bug where, in some compat modes, IE ignores leading spaces
2623
2549
  if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2624
2550
  first = false;
2625
- if (text.indexOf("\t") == -1) {
2551
+ if (!specials.test(text)) {
2626
2552
  col += text.length;
2627
- var escaped = htmlEscape(text);
2553
+ var content = document.createTextNode(text);
2628
2554
  } 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;
2555
+ var content = document.createDocumentFragment(), pos = 0;
2556
+ while (true) {
2557
+ specials.lastIndex = pos;
2558
+ var m = specials.exec(text);
2559
+ var skipped = m ? m.index - pos : text.length - pos;
2560
+ if (skipped) {
2561
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
2562
+ col += skipped;
2563
+ }
2564
+ if (!m) break;
2565
+ pos += skipped + 1;
2566
+ if (m[0] == "\t") {
2567
+ var tabWidth = tabSize - col % tabSize;
2568
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
2569
+ col += tabWidth;
2636
2570
  } else {
2637
- col += idx - pos;
2638
- var tab = makeTab(col);
2639
- escaped += htmlEscape(text.slice(pos, idx)) + tab.html;
2640
- col += tab.width;
2641
- pos = idx + 1;
2571
+ var token = elt("span", "\u2022", "cm-invalidchar");
2572
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
2573
+ content.appendChild(token);
2574
+ col += 1;
2642
2575
  }
2643
2576
  }
2644
2577
  }
2645
- if (style) html.push('<span class="', style, '">', escaped, "</span>");
2646
- else html.push(escaped);
2578
+ if (style) html.appendChild(elt("span", [content], style));
2579
+ else html.appendChild(content);
2647
2580
  }
2648
2581
  var span = span_;
2649
2582
  if (wrapAt != null) {
2650
- var outPos = 0, open = "<span id=\"" + wrapId + "\">";
2651
- span = function(text, style) {
2583
+ var outPos = 0, anchor = pre.anchor = elt("span");
2584
+ span = function(html, text, style) {
2652
2585
  var l = text.length;
2653
2586
  if (wrapAt >= outPos && wrapAt < outPos + l) {
2654
2587
  if (wrapAt > outPos) {
2655
- span_(text.slice(0, wrapAt - outPos), style);
2588
+ span_(html, text.slice(0, wrapAt - outPos), style);
2656
2589
  // See comment at the definition of spanAffectsWrapping
2657
- if (wrapWBR) html.push("<wbr>");
2590
+ if (compensateForWrapping) html.appendChild(elt("wbr"));
2658
2591
  }
2659
- html.push(open);
2592
+ html.appendChild(anchor);
2660
2593
  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);
2594
+ span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2595
+ if (opera) span_(html, text.slice(cut + 1), style);
2664
2596
  wrapAt--;
2665
2597
  outPos += l;
2666
2598
  } else {
2667
2599
  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>");
2600
+ span_(html, text, style);
2601
+ if (outPos == wrapAt && outPos == len) {
2602
+ setTextContent(anchor, eolSpanContent);
2603
+ html.appendChild(anchor);
2604
+ }
2676
2605
  // Stop outputting HTML when gone sufficiently far beyond measure
2677
2606
  else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2678
2607
  }
2679
- }
2608
+ };
2680
2609
  }
2681
2610
 
2682
- var st = this.styles, allText = this.text, marked = this.marked;
2611
+ var st = this.styles, allText = this.text, marked = this.markedSpans;
2683
2612
  var len = allText.length;
2684
2613
  function styleToClass(style) {
2685
2614
  if (!style) return null;
2686
2615
  return "cm-" + style.replace(/ +/g, " cm-");
2687
2616
  }
2688
-
2689
2617
  if (!allText && wrapAt == null) {
2690
- span(" ");
2618
+ span(pre, " ");
2691
2619
  } else if (!marked || !marked.length) {
2692
2620
  for (var i = 0, ch = 0; ch < len; i+=2) {
2693
2621
  var str = st[i], style = st[i+1], l = str.length;
2694
2622
  if (ch + l > len) str = str.slice(0, len - ch);
2695
2623
  ch += l;
2696
- span(str, styleToClass(style));
2624
+ span(pre, str, styleToClass(style));
2697
2625
  }
2698
2626
  } else {
2627
+ marked.sort(function(a, b) { return a.from - b.from; });
2699
2628
  var pos = 0, i = 0, text = "", style, sg = 0;
2700
2629
  var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2701
- function advanceMarks() {
2630
+ var advanceMarks = function() {
2702
2631
  var m;
2703
2632
  while (markpos < marked.length &&
2704
2633
  ((m = marked[markpos]).from == pos || m.from == null)) {
2705
- if (m.style != null) marks.push(m);
2634
+ if (m.marker.type == "range") marks.push(m);
2706
2635
  ++markpos;
2707
2636
  }
2708
2637
  nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
@@ -2712,7 +2641,7 @@ var CodeMirror = (function() {
2712
2641
  if (to == pos) marks.splice(i--, 1);
2713
2642
  else nextChange = Math.min(to, nextChange);
2714
2643
  }
2715
- }
2644
+ };
2716
2645
  var m = 0;
2717
2646
  while (pos < len) {
2718
2647
  if (nextChange == pos) advanceMarks();
@@ -2721,9 +2650,13 @@ var CodeMirror = (function() {
2721
2650
  if (text) {
2722
2651
  var end = pos + text.length;
2723
2652
  var appliedStyle = style;
2724
- for (var j = 0; j < marks.length; ++j)
2725
- appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2726
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2653
+ for (var j = 0; j < marks.length; ++j) {
2654
+ var mark = marks[j];
2655
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style;
2656
+ if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle;
2657
+ if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle;
2658
+ }
2659
+ span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2727
2660
  if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2728
2661
  pos = end;
2729
2662
  }
@@ -2731,28 +2664,13 @@ var CodeMirror = (function() {
2731
2664
  }
2732
2665
  }
2733
2666
  }
2734
- return html.join("");
2667
+ return pre;
2735
2668
  },
2736
2669
  cleanUp: function() {
2737
2670
  this.parent = null;
2738
- if (this.marked)
2739
- for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
2671
+ detachMarkedSpans(this);
2740
2672
  }
2741
2673
  };
2742
- // Utility used by replace and split above
2743
- function copyStyles(from, to, source, dest) {
2744
- for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
2745
- var part = source[i], end = pos + part.length;
2746
- if (state == 0) {
2747
- if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2748
- if (end >= from) state = 1;
2749
- } else if (state == 1) {
2750
- if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2751
- else dest.push(part, source[i+1]);
2752
- }
2753
- pos = end;
2754
- }
2755
- }
2756
2674
 
2757
2675
  // Data structure that holds the sequence of lines.
2758
2676
  function LeafChunk(lines) {
@@ -2953,7 +2871,7 @@ var CodeMirror = (function() {
2953
2871
  History.prototype = {
2954
2872
  addChange: function(start, added, old) {
2955
2873
  this.undone.length = 0;
2956
- var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2874
+ var time = +new Date, cur = lst(this.done), last = cur && lst(cur);
2957
2875
  var dtime = time - this.time;
2958
2876
 
2959
2877
  if (this.compound && cur && !this.closed) {
@@ -3038,30 +2956,18 @@ var CodeMirror = (function() {
3038
2956
 
3039
2957
  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
3040
2958
 
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
2959
  // Detect drag-and-drop
3054
2960
  var dragAndDrop = function() {
3055
2961
  // There is *some* kind of drag-and-drop support in IE6-8, but I
3056
2962
  // couldn't get it to work yet.
3057
2963
  if (ie_lt9) return false;
3058
- var div = document.createElement('div');
2964
+ var div = elt('div');
3059
2965
  return "draggable" in div || "dragDrop" in div;
3060
2966
  }();
3061
2967
 
3062
2968
  // Feature-detect whether newlines in textareas are converted to \r\n
3063
2969
  var lineSep = function () {
3064
- var te = document.createElement("textarea");
2970
+ var te = elt("textarea");
3065
2971
  te.value = "foo\nbar";
3066
2972
  if (te.value.indexOf("\r") > -1) return "\r\n";
3067
2973
  return "\n";
@@ -3093,11 +2999,6 @@ var CodeMirror = (function() {
3093
2999
  return n;
3094
3000
  }
3095
3001
 
3096
- function computedStyle(elt) {
3097
- if (elt.currentStyle) return elt.currentStyle;
3098
- return window.getComputedStyle(elt, null);
3099
- }
3100
-
3101
3002
  function eltOffset(node, screen) {
3102
3003
  // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
3103
3004
  // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
@@ -3116,10 +3017,19 @@ var CodeMirror = (function() {
3116
3017
  return box;
3117
3018
  }
3118
3019
 
3119
- // Get a node's text content.
3120
3020
  function eltText(node) {
3121
3021
  return node.textContent || node.innerText || node.nodeValue || "";
3122
3022
  }
3023
+
3024
+ var spaceStrs = [""];
3025
+ function spaceStr(n) {
3026
+ while (spaceStrs.length <= n)
3027
+ spaceStrs.push(lst(spaceStrs) + " ");
3028
+ return spaceStrs[n];
3029
+ }
3030
+
3031
+ function lst(arr) { return arr[arr.length-1]; }
3032
+
3123
3033
  function selectInput(node) {
3124
3034
  if (ios) { // Mobile Safari apparently has a bug where select() is broken.
3125
3035
  node.selectionStart = 0;
@@ -3132,27 +3042,27 @@ var CodeMirror = (function() {
3132
3042
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
3133
3043
  function copyPos(x) {return {line: x.line, ch: x.ch};}
3134
3044
 
3135
- var escapeElement = document.createElement("pre");
3136
- function htmlEscape(str) {
3137
- escapeElement.textContent = str;
3138
- return escapeElement.innerHTML;
3045
+ function elt(tag, content, className, style) {
3046
+ var e = document.createElement(tag);
3047
+ if (className) e.className = className;
3048
+ if (style) e.style.cssText = style;
3049
+ if (typeof content == "string") setTextContent(e, content);
3050
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
3051
+ return e;
3139
3052
  }
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
- };
3053
+ function removeChildren(e) {
3054
+ e.innerHTML = "";
3055
+ return e;
3056
+ }
3057
+ function removeChildrenAndAdd(parent, e) {
3058
+ removeChildren(parent).appendChild(e);
3059
+ }
3060
+ function setTextContent(e, str) {
3061
+ if (ie_lt9) {
3062
+ e.innerHTML = "";
3063
+ e.appendChild(document.createTextNode(str));
3064
+ } else e.textContent = str;
3154
3065
  }
3155
- CodeMirror.htmlEscape = htmlEscape;
3156
3066
 
3157
3067
  // Used to position the cursor after an undo/redo by finding the
3158
3068
  // last edited character.
@@ -3227,5 +3137,7 @@ var CodeMirror = (function() {
3227
3137
  for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
3228
3138
  })();
3229
3139
 
3140
+ CodeMirror.version = "2.34";
3141
+
3230
3142
  return CodeMirror;
3231
3143
  })();