codemirror-rails 3.00 → 3.02

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 (34) hide show
  1. data/README.md +5 -2
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/test/dummy/config/database.yml +25 -0
  4. data/test/dummy/db/.gitkeep +0 -0
  5. data/test/integration/codemirror_rails_integration_test.rb +1 -1
  6. data/vendor/assets/javascripts/codemirror.js +385 -152
  7. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +279 -66
  8. data/vendor/assets/javascripts/codemirror/modes/apl.js +160 -0
  9. data/vendor/assets/javascripts/codemirror/modes/asterisk.js +183 -0
  10. data/vendor/assets/javascripts/codemirror/modes/clike.js +2 -0
  11. data/vendor/assets/javascripts/codemirror/modes/clojure.js +3 -3
  12. data/vendor/assets/javascripts/codemirror/modes/css.js +2 -2
  13. data/vendor/assets/javascripts/codemirror/modes/d.js +205 -0
  14. data/vendor/assets/javascripts/codemirror/modes/gfm.js +2 -1
  15. data/vendor/assets/javascripts/codemirror/modes/javascript.js +13 -2
  16. data/vendor/assets/javascripts/codemirror/modes/markdown.js +8 -7
  17. data/vendor/assets/javascripts/codemirror/modes/properties.js +0 -0
  18. data/vendor/assets/javascripts/codemirror/modes/sass.js +349 -0
  19. data/vendor/assets/javascripts/codemirror/modes/sieve.js +37 -10
  20. data/vendor/assets/javascripts/codemirror/modes/sql.js +268 -0
  21. data/vendor/assets/javascripts/codemirror/modes/xquery.js +8 -8
  22. data/vendor/assets/javascripts/codemirror/utils/collapserange.js +68 -0
  23. data/vendor/assets/javascripts/codemirror/utils/dialog.js +1 -0
  24. data/vendor/assets/javascripts/codemirror/utils/foldcode.js +2 -1
  25. data/vendor/assets/javascripts/codemirror/utils/formatting.js +9 -3
  26. data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +5 -4
  27. data/vendor/assets/javascripts/codemirror/utils/python-hint.js +93 -0
  28. data/vendor/assets/javascripts/codemirror/utils/runmode-standalone.js +51 -10
  29. data/vendor/assets/javascripts/codemirror/utils/search.js +20 -8
  30. data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +17 -10
  31. data/vendor/assets/stylesheets/codemirror.css +10 -9
  32. metadata +47 -15
  33. data/vendor/assets/javascripts/codemirror/modes/xmlpure.js +0 -490
  34. data/vendor/assets/stylesheets/codemirror/modes/diff.css +0 -3
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # codemirror-rails
2
2
 
3
- Wire up the [CodeMirror](http://codemirror.net/) assets for your Rails
3
+ Wire up the [CodeMirror](http://codemirror.net/) assets for your Rails
4
4
  applications.
5
5
 
6
6
  ## Getting Started
@@ -17,7 +17,7 @@ Or manually install the codemirror-rails gem:
17
17
  gem install codemirror-rails
18
18
  ```
19
19
 
20
- ## CodeMirror for Rails 3.1
20
+ ## CodeMirror for Rails >= 3.1
21
21
 
22
22
  All of the assets from the most latest stable CodeMirror release are vendored
23
23
  so that you can use them with the asset pipeline. At a minimum, you will
@@ -67,3 +67,6 @@ assets into your Rails 3 public directory.
67
67
  ```shell
68
68
  rails generate codemirror:install
69
69
  ```
70
+ ### Contributing
71
+
72
+ Find a mistake? New version of CodeMirror? Submit a pull request!
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '3.00'
4
- CODEMIRROR_VERSION = '3.00'
3
+ VERSION = '3.02'
4
+ CODEMIRROR_VERSION = '3.02'
5
5
  end
6
6
  end
@@ -0,0 +1,25 @@
1
+ # SQLite version 3.x
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem 'sqlite3'
6
+ development:
7
+ adapter: sqlite3
8
+ database: db/development.sqlite3
9
+ pool: 5
10
+ timeout: 5000
11
+
12
+ # Warning: The database defined as "test" will be erased and
13
+ # re-generated from your development database when you run "rake".
14
+ # Do not set this db to the same as development or production.
15
+ test:
16
+ adapter: sqlite3
17
+ database: db/test.sqlite3
18
+ pool: 5
19
+ timeout: 5000
20
+
21
+ production:
22
+ adapter: sqlite3
23
+ database: db/production.sqlite3
24
+ pool: 5
25
+ timeout: 5000
File without changes
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  describe 'codemiror-rails integration' do
4
4
  it 'provides codemirror.js on the asset pipeline' do
5
5
  visit '/assets/codemirror.js'
6
- page.text.must_include 'var CodeMirror'
6
+ page.text.must_include 'window.CodeMirror'
7
7
  end
8
8
 
9
9
  it 'provides codemirror css on the asset pipeline' do
@@ -1,4 +1,4 @@
1
- // CodeMirror version 3.0
1
+ // CodeMirror version 3.02
2
2
  //
3
3
  // CodeMirror is the only global var we claim
4
4
  window.CodeMirror = (function() {
@@ -10,8 +10,8 @@ window.CodeMirror = (function() {
10
10
  // bugs and behavior differences.
11
11
  var gecko = /gecko\/\d/i.test(navigator.userAgent);
12
12
  var ie = /MSIE \d/.test(navigator.userAgent);
13
- var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
14
- var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
13
+ var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
14
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
15
15
  var webkit = /WebKit\//.test(navigator.userAgent);
16
16
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17
17
  var chrome = /Chrome\//.test(navigator.userAgent);
@@ -24,8 +24,14 @@ window.CodeMirror = (function() {
24
24
 
25
25
  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
26
26
  // This is woefully incomplete. Suggestions for alternative methods welcome.
27
- var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent);
27
+ var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
28
28
  var mac = ios || /Mac/.test(navigator.platform);
29
+ var windows = /windows/i.test(navigator.platform);
30
+
31
+ var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
32
+ if (opera_version) opera_version = Number(opera_version[1]);
33
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
34
+ var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
29
35
 
30
36
  // Optimize some code when these features are not used
31
37
  var sawReadOnlySpans = false, sawCollapsedSpans = false;
@@ -80,7 +86,9 @@ window.CodeMirror = (function() {
80
86
  function makeDisplay(place) {
81
87
  var d = {};
82
88
  var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
83
- input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
89
+ if (webkit) input.style.width = "1000px";
90
+ else input.setAttribute("wrap", "off");
91
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
84
92
  // Wraps and hides input textarea
85
93
  d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
86
94
  // The actual fake scrollbars.
@@ -91,9 +99,9 @@ window.CodeMirror = (function() {
91
99
  d.lineDiv = elt("div");
92
100
  d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
93
101
  // Blinky cursor, and element used to ensure cursor fits at the end of a line
94
- d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor");
102
+ d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
95
103
  // Secondary cursor, shown when on a 'jump' in bi-directional text
96
- d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
104
+ d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
97
105
  // Used to measure text size
98
106
  d.measure = elt("div", null, "CodeMirror-measure");
99
107
  // Wraps everything that needs to exist inside the vertically-padded coordinate system
@@ -160,6 +168,9 @@ window.CodeMirror = (function() {
160
168
  // detected
161
169
  d.pasteIncoming = false;
162
170
 
171
+ // Used for measuring wheel scrolling granularity
172
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
173
+
163
174
  return d;
164
175
  }
165
176
 
@@ -183,7 +194,9 @@ window.CodeMirror = (function() {
183
194
  suppressEdits: false,
184
195
  goalColumn: null,
185
196
  cantEdit: false,
186
- keyMaps: []
197
+ keyMaps: [],
198
+ overlays: [],
199
+ modeGen: 0
187
200
  };
188
201
  }
189
202
 
@@ -194,9 +207,14 @@ window.CodeMirror = (function() {
194
207
  function loadMode(cm) {
195
208
  var doc = cm.view.doc;
196
209
  cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
197
- doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
210
+ doc.iter(0, doc.size, function(line) {
211
+ if (line.stateAfter) line.stateAfter = null;
212
+ if (line.styles) line.styles = null;
213
+ });
198
214
  cm.view.frontier = 0;
199
215
  startWorker(cm, 100);
216
+ cm.view.modeGen++;
217
+ if (cm.curOp) regChange(cm, 0, doc.size);
200
218
  }
201
219
 
202
220
  function wrappingChanged(cm) {
@@ -342,13 +360,14 @@ window.CodeMirror = (function() {
342
360
 
343
361
  function alignHorizontally(cm) {
344
362
  var display = cm.display;
345
- if (!display.alignWidgets && !display.gutters.firstChild) return;
363
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
346
364
  var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
347
365
  var gutterW = display.gutters.offsetWidth, l = comp + "px";
348
366
  for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
349
367
  for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
350
368
  }
351
- display.gutters.style.left = (comp + gutterW) + "px";
369
+ if (cm.options.fixedGutter)
370
+ display.gutters.style.left = (comp + gutterW) + "px";
352
371
  }
353
372
 
354
373
  function maybeUpdateLineNumberWidth(cm) {
@@ -412,7 +431,8 @@ window.CodeMirror = (function() {
412
431
 
413
432
  if (changes && maybeUpdateLineNumberWidth(cm))
414
433
  changes = true;
415
- display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";
434
+ var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
435
+ display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
416
436
 
417
437
  // When merged lines are present, the line that needs to be
418
438
  // redrawn might not be the one that was changed.
@@ -458,9 +478,11 @@ window.CodeMirror = (function() {
458
478
  return;
459
479
  intact.sort(function(a, b) {return a.from - b.from;});
460
480
 
481
+ var focused = document.activeElement;
461
482
  if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
462
483
  patchDisplay(cm, from, to, intact, positionsChangedFrom);
463
484
  display.lineDiv.style.display = "";
485
+ if (document.activeElement != focused && focused.offsetHeight) focused.focus();
464
486
 
465
487
  var different = from != display.showingFrom || to != display.showingTo ||
466
488
  display.lastSizeC != display.wrapper.clientHeight;
@@ -482,12 +504,19 @@ window.CodeMirror = (function() {
482
504
  }
483
505
  var diff = node.lineObj.height - height;
484
506
  if (height < 2) height = textHeight(display);
485
- if (diff > .001 || diff < -.001)
507
+ if (diff > .001 || diff < -.001) {
486
508
  updateLineHeight(node.lineObj, height);
509
+ var widgets = node.lineObj.widgets;
510
+ if (widgets) for (var i = 0; i < widgets.length; ++i)
511
+ widgets[i].height = widgets[i].node.offsetHeight;
512
+ }
487
513
  }
488
514
  display.viewOffset = heightAtLine(cm, getLine(doc, from));
489
515
  // Position the mover div to align with the current virtual scroll position
490
516
  display.mover.style.top = display.viewOffset + "px";
517
+
518
+ if (visibleLines(display, doc, viewPort).to >= to)
519
+ updateDisplayInner(cm, [], viewPort);
491
520
  return true;
492
521
  }
493
522
 
@@ -528,9 +557,7 @@ window.CodeMirror = (function() {
528
557
  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
529
558
  var dims = getDimensions(cm);
530
559
  var display = cm.display, lineNumbers = cm.options.lineNumbers;
531
- // IE does bad things to nodes when .innerHTML = "" is used on a parent
532
- // we still need widgets and markers intact to add back to the new content later
533
- if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget))
560
+ if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
534
561
  removeChildren(display.lineDiv);
535
562
  var container = display.lineDiv, cur = container.firstChild;
536
563
 
@@ -540,7 +567,7 @@ window.CodeMirror = (function() {
540
567
  node.style.display = "none";
541
568
  node.lineObj = null;
542
569
  } else {
543
- container.removeChild(node);
570
+ node.parentNode.removeChild(node);
544
571
  }
545
572
  return next;
546
573
  }
@@ -550,6 +577,17 @@ window.CodeMirror = (function() {
550
577
  if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
551
578
  if (lineIsHidden(line)) {
552
579
  if (line.height != 0) updateLineHeight(line, 0);
580
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
581
+ if (line.widgets[i].showIfHidden) {
582
+ var prev = cur.previousSibling;
583
+ if (prev.nodeType == "pre") {
584
+ var wrap = elt("div", null, null, "position: relative");
585
+ prev.parentNode.replaceChild(wrap, prev);
586
+ wrap.appendChild(prev);
587
+ prev = wrap;
588
+ }
589
+ prev.appendChild(buildLineWidget(line.widgets[i], prev, dims));
590
+ }
553
591
  } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
554
592
  // This line is intact. Skip to the actual node. Update its
555
593
  // line number if needed.
@@ -582,8 +620,8 @@ window.CodeMirror = (function() {
582
620
  var wrap = elt("div", null, line.wrapClass, "position: relative");
583
621
  if (cm.options.lineNumbers || markers) {
584
622
  var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
585
- dims.fixedPos + "px"));
586
- wrap.alignable = [gutterWrap];
623
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
624
+ if (cm.options.fixedGutter) wrap.alignable = [gutterWrap];
587
625
  if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
588
626
  wrap.lineNumber = gutterWrap.appendChild(
589
627
  elt("div", lineNumberFor(cm.options, lineNo),
@@ -602,35 +640,38 @@ window.CodeMirror = (function() {
602
640
  if (line.bgClass)
603
641
  wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
604
642
  wrap.appendChild(lineElement);
605
- if (line.widgets)
606
- for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
607
- var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
608
- node.widget = widget;
609
- if (widget.noHScroll) {
610
- (wrap.alignable || (wrap.alignable = [])).push(node);
611
- var width = dims.wrapperWidth;
612
- node.style.left = dims.fixedPos + "px";
613
- if (!widget.coverGutter) {
614
- width -= dims.gutterTotalWidth;
615
- node.style.paddingLeft = dims.gutterTotalWidth + "px";
616
- }
617
- node.style.width = width + "px";
618
- }
619
- if (widget.coverGutter) {
620
- node.style.zIndex = 5;
621
- node.style.position = "relative";
622
- if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
623
- }
624
- if (widget.above)
625
- wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
626
- else
627
- wrap.appendChild(node);
628
- }
629
-
643
+ if (line.widgets) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
644
+ var widget = ws[i], node = buildLineWidget(widget, wrap, dims);
645
+ if (widget.above)
646
+ wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
647
+ else
648
+ wrap.appendChild(node);
649
+ }
630
650
  if (ie_lt8) wrap.style.zIndex = 2;
631
651
  return wrap;
632
652
  }
633
653
 
654
+ function buildLineWidget(widget, wrap, dims) {
655
+ var node = elt("div", [widget.node], "CodeMirror-linewidget");
656
+ node.widget = widget;
657
+ if (widget.noHScroll) {
658
+ (wrap.alignable || (wrap.alignable = [])).push(node);
659
+ var width = dims.wrapperWidth;
660
+ node.style.left = dims.fixedPos + "px";
661
+ if (!widget.coverGutter) {
662
+ width -= dims.gutterTotalWidth;
663
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
664
+ }
665
+ node.style.width = width + "px";
666
+ }
667
+ if (widget.coverGutter) {
668
+ node.style.zIndex = 5;
669
+ node.style.position = "relative";
670
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
671
+ }
672
+ return node;
673
+ }
674
+
634
675
  // SELECTION / CURSOR
635
676
 
636
677
  function updateSelection(cm) {
@@ -765,7 +806,7 @@ window.CodeMirror = (function() {
765
806
  // HIGHLIGHT WORKER
766
807
 
767
808
  function startWorker(cm, time) {
768
- if (cm.view.frontier < cm.display.showingTo)
809
+ if (cm.view.mode.startState && cm.view.frontier < cm.display.showingTo)
769
810
  cm.view.highlight.set(time, bind(highlightWorker, cm));
770
811
  }
771
812
 
@@ -777,7 +818,12 @@ window.CodeMirror = (function() {
777
818
  var changed = [], prevChange;
778
819
  doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
779
820
  if (view.frontier >= cm.display.showingFrom) { // Visible
780
- if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) {
821
+ var oldStyles = line.styles;
822
+ line.styles = highlightLine(cm, line, state);
823
+ var ischange = !oldStyles || oldStyles.length != line.styles.length;
824
+ for (var i = 0; !ischange && i < oldStyles.length; ++i)
825
+ ischange = oldStyles[i] != line.styles[i];
826
+ if (ischange) {
781
827
  if (prevChange && prevChange.end == view.frontier) prevChange.end++;
782
828
  else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
783
829
  }
@@ -821,6 +867,7 @@ window.CodeMirror = (function() {
821
867
 
822
868
  function getStateBefore(cm, n) {
823
869
  var view = cm.view;
870
+ if (!view.mode.startState) return true;
824
871
  var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
825
872
  if (!state) state = startState(view.mode);
826
873
  else state = copyState(view.mode, state);
@@ -842,7 +889,9 @@ window.CodeMirror = (function() {
842
889
  }
843
890
 
844
891
  function measureChar(cm, line, ch, data) {
845
- var data = data || measureLine(cm, line), dir = -1;
892
+ var dir = -1;
893
+ data = data || measureLine(cm, line);
894
+
846
895
  for (var pos = ch;; pos += dir) {
847
896
  var r = data[pos];
848
897
  if (r) break;
@@ -938,7 +987,7 @@ window.CodeMirror = (function() {
938
987
  // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
939
988
  function intoCoordSystem(cm, lineObj, rect, context) {
940
989
  if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
941
- var size = lineObj.widgets[i].node.offsetHeight;
990
+ var size = widgetHeight(lineObj.widgets[i]);
942
991
  rect.top += size; rect.bottom += size;
943
992
  }
944
993
  if (context == "line") return rect;
@@ -1010,8 +1059,9 @@ window.CodeMirror = (function() {
1010
1059
  var lineObj = getLine(doc, lineNo);
1011
1060
  var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1012
1061
  var merged = collapsedSpanAtEnd(lineObj);
1013
- if (merged && found.ch == lineRight(lineObj))
1014
- lineNo = merged.find().to.line;
1062
+ var mergedPos = merged && merged.find();
1063
+ if (merged && found.ch >= mergedPos.from.ch)
1064
+ lineNo = mergedPos.to.line;
1015
1065
  else
1016
1066
  return found;
1017
1067
  }
@@ -1121,6 +1171,9 @@ window.CodeMirror = (function() {
1121
1171
  var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
1122
1172
  display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
1123
1173
  view.maxLineChanged = false;
1174
+ var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1175
+ if (maxScrollLeft < view.scrollLeft)
1176
+ setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1124
1177
  }
1125
1178
  var newScrollPos, updated;
1126
1179
  if (op.selectionChanged) {
@@ -1237,7 +1290,7 @@ window.CodeMirror = (function() {
1237
1290
  on(d.scroller, "mousedown", operation(cm, onMouseDown));
1238
1291
  on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1239
1292
  on(d.lineSpace, "selectstart", function(e) {
1240
- if (!mouseEventInWidget(d, e)) e_preventDefault(e);
1293
+ if (!eventInWidget(d, e)) e_preventDefault(e);
1241
1294
  });
1242
1295
  // Gecko browsers fire contextmenu *after* opening the menu, at
1243
1296
  // which point we can't mess with it anymore. Context menu is
@@ -1264,13 +1317,24 @@ window.CodeMirror = (function() {
1264
1317
  on(d.scrollbarV, "mousedown", reFocus);
1265
1318
  // Prevent wrapper from ever scrolling
1266
1319
  on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1267
- on(window, "resize", function resizeHandler() {
1320
+
1321
+ if (!window.registered) window.registered = 0;
1322
+ ++window.registered;
1323
+ function onResize() {
1268
1324
  // Might be a text scaling operation, clear size caches.
1269
1325
  d.cachedCharWidth = d.cachedTextHeight = null;
1270
1326
  clearCaches(cm);
1271
- if (d.wrapper.parentNode) updateDisplay(cm, true);
1272
- else off(window, "resize", resizeHandler);
1273
- });
1327
+ updateDisplay(cm, true);
1328
+ }
1329
+ on(window, "resize", onResize);
1330
+ // Above handler holds on to the editor and its data structures.
1331
+ // Here we poll to unregister it when the editor is no longer in
1332
+ // the document, so that it can be garbage-collected.
1333
+ setTimeout(function unregister() {
1334
+ for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1335
+ if (p) setTimeout(unregister, 5000);
1336
+ else {--window.registered; off(window, "resize", onResize);}
1337
+ }, 5000);
1274
1338
 
1275
1339
  on(d.input, "keyup", operation(cm, function(e) {
1276
1340
  if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
@@ -1292,7 +1356,11 @@ window.CodeMirror = (function() {
1292
1356
  on(d.scroller, "dragover", drag_);
1293
1357
  on(d.scroller, "drop", operation(cm, onDrop));
1294
1358
  }
1295
- on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);});
1359
+ on(d.scroller, "paste", function(e){
1360
+ if (eventInWidget(d, e)) return;
1361
+ focusInput(cm);
1362
+ fastPoll(cm);
1363
+ });
1296
1364
  on(d.input, "paste", function() {
1297
1365
  d.pasteIncoming = true;
1298
1366
  fastPoll(cm);
@@ -1316,10 +1384,12 @@ window.CodeMirror = (function() {
1316
1384
  });
1317
1385
  }
1318
1386
 
1319
- function mouseEventInWidget(display, e) {
1320
- for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
1387
+ function eventInWidget(display, e) {
1388
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1389
+ if (!n) return true;
1321
1390
  if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1322
1391
  n.parentNode == display.sizer && n != display.mover) return true;
1392
+ }
1323
1393
  }
1324
1394
 
1325
1395
  function posFromMouse(cm, e, liberal) {
@@ -1341,7 +1411,7 @@ window.CodeMirror = (function() {
1341
1411
  var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
1342
1412
  sel.shift = e_prop(e, "shiftKey");
1343
1413
 
1344
- if (mouseEventInWidget(display, e)) {
1414
+ if (eventInWidget(display, e)) {
1345
1415
  if (!webkit) {
1346
1416
  display.scroller.draggable = false;
1347
1417
  setTimeout(function(){display.scroller.draggable = true;}, 100);
@@ -1477,7 +1547,8 @@ window.CodeMirror = (function() {
1477
1547
 
1478
1548
  function onDrop(e) {
1479
1549
  var cm = this;
1480
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1550
+ if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1551
+ return;
1481
1552
  e_preventDefault(e);
1482
1553
  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1483
1554
  if (!pos || isReadOnly(cm)) return;
@@ -1502,7 +1573,8 @@ window.CodeMirror = (function() {
1502
1573
  // Don't do a replace if the drop happened inside of the selected text.
1503
1574
  if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
1504
1575
  cm.view.draggingText(e);
1505
- if (ie) setTimeout(bind(focusInput, cm), 50);
1576
+ // Ensure the editor is re-focused
1577
+ setTimeout(bind(focusInput, cm), 20);
1506
1578
  return;
1507
1579
  }
1508
1580
  try {
@@ -1546,13 +1618,24 @@ window.CodeMirror = (function() {
1546
1618
  }
1547
1619
 
1548
1620
  function onDragStart(cm, e) {
1621
+ if (eventInWidget(cm.display, e)) return;
1622
+
1549
1623
  var txt = cm.getSelection();
1550
1624
  e.dataTransfer.setData("Text", txt);
1551
1625
 
1552
1626
  // Use dummy image instead of default browsers image.
1553
1627
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1554
- if (e.dataTransfer.setDragImage && !safari)
1555
- e.dataTransfer.setDragImage(elt('img'), 0, 0);
1628
+ if (e.dataTransfer.setDragImage && !safari) {
1629
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1630
+ if (opera) {
1631
+ img.width = img.height = 1;
1632
+ cm.display.wrapper.appendChild(img);
1633
+ // Force a relayout, or Opera won't use our image for some obscure reason
1634
+ img._top = img.offsetTop;
1635
+ }
1636
+ e.dataTransfer.setDragImage(img, 0, 0);
1637
+ if (opera) img.parentNode.removeChild(img);
1638
+ }
1556
1639
  }
1557
1640
 
1558
1641
  function setScrollTop(cm, val) {
@@ -1565,6 +1648,7 @@ window.CodeMirror = (function() {
1565
1648
  }
1566
1649
  function setScrollLeft(cm, val, isScroller) {
1567
1650
  if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
1651
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1568
1652
  cm.view.scrollLeft = val;
1569
1653
  alignHorizontally(cm);
1570
1654
  if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
@@ -1582,7 +1666,7 @@ window.CodeMirror = (function() {
1582
1666
  // is that it gives us a chance to update the display before the
1583
1667
  // actual scrolling happens, reducing flickering.
1584
1668
 
1585
- var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null;
1669
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
1586
1670
  // Fill in a browser-detected starting value on browsers where we
1587
1671
  // know one. These don't have to be accurate -- the result of them
1588
1672
  // being wrong would just be a slight flicker on the first wheel
@@ -1611,7 +1695,7 @@ window.CodeMirror = (function() {
1611
1695
  }
1612
1696
  }
1613
1697
 
1614
- var scroll = cm.display.scroller;
1698
+ var display = cm.display, scroll = display.scroller;
1615
1699
  // On some browsers, horizontal scrolling will cause redraws to
1616
1700
  // happen before the gutter has been realigned, causing it to
1617
1701
  // wriggle around in a most unseemly way. When we have an
@@ -1623,35 +1707,35 @@ window.CodeMirror = (function() {
1623
1707
  setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1624
1708
  setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1625
1709
  e_preventDefault(e);
1626
- wheelStartX = null; // Abort measurement, if in progress
1710
+ display.wheelStartX = null; // Abort measurement, if in progress
1627
1711
  return;
1628
1712
  }
1629
1713
 
1630
1714
  if (dy && wheelPixelsPerUnit != null) {
1631
1715
  var pixels = dy * wheelPixelsPerUnit;
1632
- var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight;
1716
+ var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight;
1633
1717
  if (pixels < 0) top = Math.max(0, top + pixels - 50);
1634
1718
  else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
1635
1719
  updateDisplay(cm, [], {top: top, bottom: bot});
1636
1720
  }
1637
1721
 
1638
1722
  if (wheelSamples < 20) {
1639
- if (wheelStartX == null) {
1640
- wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
1641
- wheelDX = dx; wheelDY = dy;
1723
+ if (display.wheelStartX == null) {
1724
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1725
+ display.wheelDX = dx; display.wheelDY = dy;
1642
1726
  setTimeout(function() {
1643
- if (wheelStartX == null) return;
1644
- var movedX = scroll.scrollLeft - wheelStartX;
1645
- var movedY = scroll.scrollTop - wheelStartY;
1646
- var sample = (movedY && wheelDY && movedY / wheelDY) ||
1647
- (movedX && wheelDX && movedX / wheelDX);
1648
- wheelStartX = wheelStartY = null;
1727
+ if (display.wheelStartX == null) return;
1728
+ var movedX = scroll.scrollLeft - display.wheelStartX;
1729
+ var movedY = scroll.scrollTop - display.wheelStartY;
1730
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1731
+ (movedX && display.wheelDX && movedX / display.wheelDX);
1732
+ display.wheelStartX = display.wheelStartY = null;
1649
1733
  if (!sample) return;
1650
1734
  wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1651
1735
  ++wheelSamples;
1652
1736
  }, 200);
1653
1737
  } else {
1654
- wheelDX += dx; wheelDY += dy;
1738
+ display.wheelDX += dx; display.wheelDY += dy;
1655
1739
  }
1656
1740
  }
1657
1741
  }
@@ -1697,7 +1781,6 @@ window.CodeMirror = (function() {
1697
1781
  }, 50);
1698
1782
 
1699
1783
  var name = keyNames[e_prop(e, "keyCode")], handled = false;
1700
- var flipCtrlCmd = mac && (opera || qtwebkit);
1701
1784
  if (name == null || e.altGraphKey) return false;
1702
1785
  if (e_prop(e, "altKey")) name = "Alt-" + name;
1703
1786
  if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
@@ -1794,7 +1877,10 @@ window.CodeMirror = (function() {
1794
1877
 
1795
1878
  var detectingSelectAll;
1796
1879
  function onContextMenu(cm, e) {
1797
- var display = cm.display, sel = cm.view.sel;
1880
+ var display = cm.display;
1881
+ if (eventInWidget(display, e)) return;
1882
+
1883
+ var sel = cm.view.sel;
1798
1884
  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1799
1885
  if (!pos || opera) return; // Opera is difficult.
1800
1886
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
@@ -1904,7 +1990,7 @@ window.CodeMirror = (function() {
1904
1990
  if (!cm.options.lineWrapping) {
1905
1991
  checkWidthStart = lineNo(visualLine(doc, firstLine));
1906
1992
  doc.iter(checkWidthStart, to.line + 1, function(line) {
1907
- if (lineLength(doc, line) == view.maxLineLength) {
1993
+ if (line == view.maxLine) {
1908
1994
  recomputeMaxLength = true;
1909
1995
  return true;
1910
1996
  }
@@ -2351,6 +2437,25 @@ window.CodeMirror = (function() {
2351
2437
  }
2352
2438
  },
2353
2439
 
2440
+ addOverlay: operation(null, function(spec, options) {
2441
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2442
+ if (mode.startState) throw new Error("Overlays may not be stateful.");
2443
+ this.view.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2444
+ this.view.modeGen++;
2445
+ regChange(this, 0, this.view.doc.size);
2446
+ }),
2447
+ removeOverlay: operation(null, function(spec) {
2448
+ var overlays = this.view.overlays;
2449
+ for (var i = 0; i < overlays.length; ++i) {
2450
+ if (overlays[i].modeSpec == spec) {
2451
+ overlays.splice(i, 1);
2452
+ this.view.modeGen++;
2453
+ regChange(this, 0, this.view.doc.size);
2454
+ return;
2455
+ }
2456
+ }
2457
+ }),
2458
+
2354
2459
  undo: operation(null, function() {unredoHelper(this, "undo");}),
2355
2460
  redo: operation(null, function() {unredoHelper(this, "redo");}),
2356
2461
 
@@ -2522,23 +2627,10 @@ window.CodeMirror = (function() {
2522
2627
  }),
2523
2628
 
2524
2629
  addLineWidget: operation(null, function(handle, node, options) {
2525
- var widget = options || {};
2526
- widget.node = node;
2527
- if (widget.noHScroll) this.display.alignWidgets = true;
2528
- changeLine(this, handle, function(line) {
2529
- (line.widgets || (line.widgets = [])).push(widget);
2530
- widget.line = line;
2531
- return true;
2532
- });
2533
- return widget;
2630
+ return addLineWidget(this, handle, node, options);
2534
2631
  }),
2535
2632
 
2536
- removeLineWidget: operation(null, function(widget) {
2537
- var ws = widget.line.widgets, no = lineNo(widget.line);
2538
- if (no == null) return;
2539
- for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
2540
- regChange(this, no, no + 1);
2541
- }),
2633
+ removeLineWidget: function(widget) { widget.clear(); },
2542
2634
 
2543
2635
  lineInfo: function(line) {
2544
2636
  if (typeof line == "number") {
@@ -2663,7 +2755,8 @@ window.CodeMirror = (function() {
2663
2755
  // Stuff used by commands, probably not much use to outside code.
2664
2756
  moveH: operation(null, function(dir, unit) {
2665
2757
  var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
2666
- if (sel.shift || sel.extend || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true);
2758
+ if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2759
+ pos = findPosH(this, dir, unit, this.options.rtlMoveVisually);
2667
2760
  extendSelection(this, pos, pos, dir);
2668
2761
  }),
2669
2762
 
@@ -2713,7 +2806,7 @@ window.CodeMirror = (function() {
2713
2806
  return clipPos(doc, {line: lineNo, ch: ch});
2714
2807
  },
2715
2808
  indexFromPos: function (coords) {
2716
- if (coords.line < 0 || coords.ch < 0) return 0;
2809
+ coords = clipPos(this.view.doc, coords);
2717
2810
  var index = coords.ch;
2718
2811
  this.view.doc.iter(0, coords.line, function (line) {
2719
2812
  index += line.text.length + 1;
@@ -2735,8 +2828,12 @@ window.CodeMirror = (function() {
2735
2828
 
2736
2829
  scrollIntoView: function(pos) {
2737
2830
  if (typeof pos == "number") pos = {line: pos, ch: 0};
2738
- pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
2739
- scrollPosIntoView(this, pos);
2831
+ if (!pos || pos.line != null) {
2832
+ pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
2833
+ scrollPosIntoView(this, pos);
2834
+ } else {
2835
+ scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom);
2836
+ }
2740
2837
  },
2741
2838
 
2742
2839
  setSize: function(width, height) {
@@ -2755,8 +2852,11 @@ window.CodeMirror = (function() {
2755
2852
 
2756
2853
  refresh: function() {
2757
2854
  clearCaches(this);
2758
- if (this.display.scroller.scrollHeight > this.view.scrollTop)
2759
- this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop;
2855
+ var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft;
2856
+ if (this.display.scroller.scrollHeight > sTop)
2857
+ this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop;
2858
+ if (this.display.scroller.scrollWidth > sLeft)
2859
+ this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = sLeft;
2760
2860
  updateDisplay(this, true);
2761
2861
  },
2762
2862
 
@@ -2795,6 +2895,7 @@ window.CodeMirror = (function() {
2795
2895
  updateDisplay(cm, true);
2796
2896
  }, true);
2797
2897
  option("electricChars", true);
2898
+ option("rtlMoveVisually", !windows);
2798
2899
 
2799
2900
  option("theme", "default", function(cm) {
2800
2901
  themeChanged(cm);
@@ -2811,6 +2912,10 @@ window.CodeMirror = (function() {
2811
2912
  setGuttersForLineNumbers(cm.options);
2812
2913
  guttersChanged(cm);
2813
2914
  }, true);
2915
+ option("fixedGutter", true, function(cm, val) {
2916
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
2917
+ cm.refresh();
2918
+ }, true);
2814
2919
  option("lineNumbers", false, function(cm) {
2815
2920
  setGuttersForLineNumbers(cm.options);
2816
2921
  guttersChanged(cm);
@@ -2867,7 +2972,7 @@ window.CodeMirror = (function() {
2867
2972
  };
2868
2973
 
2869
2974
  CodeMirror.getMode = function(options, spec) {
2870
- var spec = CodeMirror.resolveMode(spec);
2975
+ spec = CodeMirror.resolveMode(spec);
2871
2976
  var mfactory = modes[spec.name];
2872
2977
  if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2873
2978
  var modeObj = mfactory(options, spec);
@@ -3204,11 +3309,12 @@ window.CodeMirror = (function() {
3204
3309
  this.type = type;
3205
3310
  this.cm = cm;
3206
3311
  }
3312
+ CodeMirror.TextMarker = TextMarker;
3207
3313
 
3208
3314
  TextMarker.prototype.clear = function() {
3209
3315
  if (this.explicitlyCleared) return;
3210
3316
  startOperation(this.cm);
3211
- var min = null, max = null;
3317
+ var view = this.cm.view, min = null, max = null;
3212
3318
  for (var i = 0; i < this.lines.length; ++i) {
3213
3319
  var line = this.lines[i];
3214
3320
  var span = getMarkedSpanFor(line.markedSpans, this);
@@ -3219,6 +3325,15 @@ window.CodeMirror = (function() {
3219
3325
  else if (this.collapsed && !lineIsHidden(line))
3220
3326
  updateLineHeight(line, textHeight(this.cm.display));
3221
3327
  }
3328
+ if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3329
+ var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual);
3330
+ if (len > view.maxLineLength) {
3331
+ view.maxLine = visual;
3332
+ view.maxLineLength = len;
3333
+ view.maxLineChanged = true;
3334
+ }
3335
+ }
3336
+
3222
3337
  if (min != null) regChange(this.cm, min, max + 1);
3223
3338
  this.lines.length = 0;
3224
3339
  this.explicitlyCleared = true;
@@ -3245,6 +3360,18 @@ window.CodeMirror = (function() {
3245
3360
  return from && {from: from, to: to};
3246
3361
  };
3247
3362
 
3363
+ TextMarker.prototype.getOptions = function(copyWidget) {
3364
+ var repl = this.replacedWith;
3365
+ return {className: this.className,
3366
+ inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3367
+ atomic: this.atomic,
3368
+ collapsed: this.collapsed,
3369
+ clearOnEnter: this.clearOnEnter,
3370
+ replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3371
+ readOnly: this.readOnly,
3372
+ startStyle: this.startStyle, endStyle: this.endStyle};
3373
+ };
3374
+
3248
3375
  function markText(cm, from, to, options, type) {
3249
3376
  var doc = cm.view.doc;
3250
3377
  var marker = new TextMarker(cm, type);
@@ -3259,6 +3386,8 @@ window.CodeMirror = (function() {
3259
3386
 
3260
3387
  var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
3261
3388
  doc.iter(curLine, to.line + 1, function(line) {
3389
+ if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine)
3390
+ cm.curOp.updateMaxLine = true;
3262
3391
  var span = {from: null, to: null, marker: marker};
3263
3392
  size += line.text.length;
3264
3393
  if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
@@ -3269,10 +3398,11 @@ window.CodeMirror = (function() {
3269
3398
  else updateLineHeight(line, 0);
3270
3399
  }
3271
3400
  addMarkedSpan(line, span);
3272
- if (marker.collapsed && curLine == from.line && lineIsHidden(line))
3273
- updateLineHeight(line, 0);
3274
3401
  ++curLine;
3275
3402
  });
3403
+ if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3404
+ if (lineIsHidden(line)) updateLineHeight(line, 0);
3405
+ });
3276
3406
 
3277
3407
  if (marker.readOnly) {
3278
3408
  sawReadOnlySpans = true;
@@ -3447,9 +3577,12 @@ window.CodeMirror = (function() {
3447
3577
  return true;
3448
3578
  }
3449
3579
  }
3450
- window.lineIsHidden = lineIsHidden;
3451
3580
  function lineIsHiddenInner(line, span) {
3452
- if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length)
3581
+ if (span.to == null) {
3582
+ var end = span.marker.find().to, endLine = getLine(lineDoc(line), end.line);
3583
+ return lineIsHiddenInner(endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3584
+ }
3585
+ if (span.marker.inclusiveRight && span.to == line.text.length)
3453
3586
  return true;
3454
3587
  for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3455
3588
  sp = line.markedSpans[i];
@@ -3491,6 +3624,63 @@ window.CodeMirror = (function() {
3491
3624
  line.markedSpans = spans;
3492
3625
  }
3493
3626
 
3627
+ // LINE WIDGETS
3628
+
3629
+ var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3630
+ for (var opt in options) if (options.hasOwnProperty(opt))
3631
+ this[opt] = options[opt];
3632
+ this.cm = cm;
3633
+ this.node = node;
3634
+ };
3635
+ function widgetOperation(f) {
3636
+ return function() {
3637
+ startOperation(this.cm);
3638
+ try {var result = f.apply(this, arguments);}
3639
+ finally {endOperation(this.cm);}
3640
+ return result;
3641
+ };
3642
+ }
3643
+ LineWidget.prototype.clear = widgetOperation(function() {
3644
+ var ws = this.line.widgets, no = lineNo(this.line);
3645
+ if (no == null || !ws) return;
3646
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3647
+ updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3648
+ regChange(this.cm, no, no + 1);
3649
+ });
3650
+ LineWidget.prototype.changed = widgetOperation(function() {
3651
+ var oldH = this.height;
3652
+ this.height = null;
3653
+ var diff = widgetHeight(this) - oldH;
3654
+ if (!diff) return;
3655
+ updateLineHeight(this.line, this.line.height + diff);
3656
+ var no = lineNo(this.line);
3657
+ regChange(this.cm, no, no + 1);
3658
+ });
3659
+
3660
+ function widgetHeight(widget) {
3661
+ if (widget.height != null) return widget.height;
3662
+ if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3663
+ removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3664
+ return widget.height = widget.node.offsetHeight;
3665
+ }
3666
+
3667
+ function addLineWidget(cm, handle, node, options) {
3668
+ var widget = new LineWidget(cm, node, options);
3669
+ if (widget.noHScroll) cm.display.alignWidgets = true;
3670
+ changeLine(cm, handle, function(line) {
3671
+ (line.widgets || (line.widgets = [])).push(widget);
3672
+ widget.line = line;
3673
+ if (!lineIsHidden(line) || widget.showIfHidden) {
3674
+ var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3675
+ updateLineHeight(line, line.height + widgetHeight(widget));
3676
+ if (aboveVisible)
3677
+ setTimeout(function() {cm.display.scroller.scrollTop += widget.height;});
3678
+ }
3679
+ return true;
3680
+ });
3681
+ return widget;
3682
+ }
3683
+
3494
3684
  // LINE DATA STRUCTURE
3495
3685
 
3496
3686
  // Line objects. These hold state related to a line, including
@@ -3504,7 +3694,8 @@ window.CodeMirror = (function() {
3504
3694
 
3505
3695
  function updateLine(cm, line, text, markedSpans) {
3506
3696
  line.text = text;
3507
- line.stateAfter = line.styles = null;
3697
+ if (line.stateAfter) line.stateAfter = null;
3698
+ if (line.styles) line.styles = null;
3508
3699
  if (line.order != null) line.order = null;
3509
3700
  detachMarkedSpans(line);
3510
3701
  attachMarkedSpans(line, markedSpans);
@@ -3521,31 +3712,72 @@ window.CodeMirror = (function() {
3521
3712
  // Run the given mode's parser over a line, update the styles
3522
3713
  // array, which contains alternating fragments of text and CSS
3523
3714
  // classes.
3524
- function highlightLine(cm, line, state) {
3525
- var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans;
3526
- var changed = !line.styles, pos = 0, curText = "", curStyle = null;
3527
- var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []);
3528
- if (line.text == "" && mode.blankLine) mode.blankLine(state);
3715
+ function runMode(cm, text, mode, state, f) {
3716
+ var flattenSpans = cm.options.flattenSpans;
3717
+ var curText = "", curStyle = null;
3718
+ var stream = new StringStream(text, cm.options.tabSize);
3719
+ if (text == "" && mode.blankLine) mode.blankLine(state);
3529
3720
  while (!stream.eol()) {
3530
- var style = mode.token(stream, state), substr = stream.current();
3721
+ var style = mode.token(stream, state);
3722
+ if (stream.pos > 5000) {
3723
+ flattenSpans = false;
3724
+ // Webkit seems to refuse to render text nodes longer than 57444 characters
3725
+ stream.pos = Math.min(text.length, stream.start + 50000);
3726
+ style = null;
3727
+ }
3728
+ var substr = stream.current();
3531
3729
  stream.start = stream.pos;
3532
3730
  if (!flattenSpans || curStyle != style) {
3533
- if (curText) {
3534
- changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
3535
- st[pos++] = curText; st[pos++] = curStyle;
3536
- }
3731
+ if (curText) f(curText, curStyle);
3537
3732
  curText = substr; curStyle = style;
3538
3733
  } else curText = curText + substr;
3539
- // Give up when line is ridiculously long
3540
- if (stream.pos > 5000) break;
3541
3734
  }
3542
- if (curText) {
3543
- changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
3544
- st[pos++] = curText; st[pos++] = curStyle;
3735
+ if (curText) f(curText, curStyle);
3736
+ }
3737
+
3738
+ function highlightLine(cm, line, state) {
3739
+ // A styles array always starts with a number identifying the
3740
+ // mode/overlays that it is based on (for easy invalidation).
3741
+ var st = [cm.view.modeGen];
3742
+ // Compute the base array of styles
3743
+ runMode(cm, line.text, cm.view.mode, state, function(txt, style) {st.push(txt, style);});
3744
+
3745
+ // Run overlays, adjust style array.
3746
+ for (var o = 0; o < cm.view.overlays.length; ++o) {
3747
+ var overlay = cm.view.overlays[o], i = 1;
3748
+ runMode(cm, line.text, overlay.mode, true, function(txt, style) {
3749
+ var start = i, len = txt.length;
3750
+ // Ensure there's a token end at the current position, and that i points at it
3751
+ while (len) {
3752
+ var cur = st[i], len_ = cur.length;
3753
+ if (len_ <= len) {
3754
+ len -= len_;
3755
+ } else {
3756
+ st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
3757
+ len = 0;
3758
+ }
3759
+ i += 2;
3760
+ }
3761
+ if (!style) return;
3762
+ if (overlay.opaque) {
3763
+ st.splice(start, i - start, txt, style);
3764
+ i = start + 2;
3765
+ } else {
3766
+ for (; start < i; start += 2) {
3767
+ var cur = st[start+1];
3768
+ st[start+1] = cur ? cur + " " + style : style;
3769
+ }
3770
+ }
3771
+ });
3545
3772
  }
3546
- if (stream.pos > 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; }
3547
- if (pos != st.length) { st.length = pos; changed = true; }
3548
- return changed;
3773
+
3774
+ return st;
3775
+ }
3776
+
3777
+ function getLineStyles(cm, line) {
3778
+ if (!line.styles || line.styles[0] != cm.view.modeGen)
3779
+ line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
3780
+ return line.styles;
3549
3781
  }
3550
3782
 
3551
3783
  // Lightweight form of highlight -- proceed over this line and
@@ -3580,8 +3812,6 @@ window.CodeMirror = (function() {
3580
3812
  if (line.textClass) builder.pre.className = line.textClass;
3581
3813
 
3582
3814
  do {
3583
- if (!line.styles)
3584
- highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
3585
3815
  builder.measure = line == realLine && measure;
3586
3816
  builder.pos = 0;
3587
3817
  builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
@@ -3589,7 +3819,7 @@ window.CodeMirror = (function() {
3589
3819
  measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
3590
3820
  builder.addedOne = true;
3591
3821
  }
3592
- var next = insertLineContent(line, builder);
3822
+ var next = insertLineContent(line, builder, getLineStyles(cm, line));
3593
3823
  sawBefore = line == lineBefore;
3594
3824
  if (next) {
3595
3825
  line = getLine(cm.view.doc, next.to.line);
@@ -3646,7 +3876,7 @@ window.CodeMirror = (function() {
3646
3876
 
3647
3877
  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
3648
3878
  for (var i = 0; i < text.length; ++i) {
3649
- if (i && i < text.length - 1 &&
3879
+ if (i && i < text.length &&
3650
3880
  builder.cm.options.lineWrapping &&
3651
3881
  spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
3652
3882
  builder.pre.appendChild(elt("wbr"));
@@ -3671,16 +3901,16 @@ window.CodeMirror = (function() {
3671
3901
 
3672
3902
  // Outputs a number of spans to make up a line, taking highlighting
3673
3903
  // and marked text into account.
3674
- function insertLineContent(line, builder) {
3675
- var st = line.styles, spans = line.markedSpans;
3904
+ function insertLineContent(line, builder, styles) {
3905
+ var spans = line.markedSpans;
3676
3906
  if (!spans) {
3677
- for (var i = 0; i < st.length; i+=2)
3678
- builder.addToken(builder, st[i], styleToClass(st[i+1]));
3907
+ for (var i = 1; i < styles.length; i+=2)
3908
+ builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
3679
3909
  return;
3680
3910
  }
3681
3911
 
3682
3912
  var allText = line.text, len = allText.length;
3683
- var pos = 0, i = 0, text = "", style;
3913
+ var pos = 0, i = 1, text = "", style;
3684
3914
  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
3685
3915
  for (;;) {
3686
3916
  if (nextChange == pos) { // Update current marker set
@@ -3724,7 +3954,7 @@ window.CodeMirror = (function() {
3724
3954
  pos = end;
3725
3955
  spanStartStyle = "";
3726
3956
  }
3727
- text = st[i++]; style = styleToClass(st[i++]);
3957
+ text = styles[i++]; style = styleToClass(styles[i++]);
3728
3958
  }
3729
3959
  }
3730
3960
  }
@@ -3896,6 +4126,11 @@ window.CodeMirror = (function() {
3896
4126
  return no;
3897
4127
  }
3898
4128
 
4129
+ function lineDoc(line) {
4130
+ for (var d = line.parent; d.parent; d = d.parent) {}
4131
+ return d;
4132
+ }
4133
+
3899
4134
  function lineAtHeight(chunk, h) {
3900
4135
  var n = 0;
3901
4136
  outer: do {
@@ -4177,7 +4412,9 @@ window.CodeMirror = (function() {
4177
4412
  }
4178
4413
 
4179
4414
  function removeChildren(e) {
4180
- e.innerHTML = "";
4415
+ // IE will break all parent-child relations in subnodes when setting innerHTML
4416
+ if (!ie) e.innerHTML = "";
4417
+ else while (e.firstChild) e.removeChild(e.firstChild);
4181
4418
  return e;
4182
4419
  }
4183
4420
 
@@ -4300,7 +4537,7 @@ window.CodeMirror = (function() {
4300
4537
  if (!order) return f(from, to, "ltr");
4301
4538
  for (var i = 0; i < order.length; ++i) {
4302
4539
  var part = order[i];
4303
- if (part.from < to && part.to > from)
4540
+ if (part.from < to && part.to > from || from == to && part.to == from)
4304
4541
  f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
4305
4542
  }
4306
4543
  }
@@ -4418,24 +4655,20 @@ window.CodeMirror = (function() {
4418
4655
 
4419
4656
  var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
4420
4657
  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
4658
+ // Browsers seem to always treat the boundaries of block elements as being L.
4659
+ var outerType = "L";
4421
4660
 
4422
4661
  return function charOrdering(str) {
4423
4662
  if (!bidiRE.test(str)) return false;
4424
- var len = str.length, types = [], startType = null;
4425
- for (var i = 0, type; i < len; ++i) {
4663
+ var len = str.length, types = [];
4664
+ for (var i = 0, type; i < len; ++i)
4426
4665
  types.push(type = charType(str.charCodeAt(i)));
4427
- if (startType == null) {
4428
- if (type == "L") startType = "L";
4429
- else if (type == "R" || type == "r") startType = "R";
4430
- }
4431
- }
4432
- if (startType == null) startType = "L";
4433
4666
 
4434
4667
  // W1. Examine each non-spacing mark (NSM) in the level run, and
4435
4668
  // change the type of the NSM to the type of the previous
4436
4669
  // character. If the NSM is at the start of the level run, it will
4437
4670
  // get the type of sor.
4438
- for (var i = 0, prev = startType; i < len; ++i) {
4671
+ for (var i = 0, prev = outerType; i < len; ++i) {
4439
4672
  var type = types[i];
4440
4673
  if (type == "m") types[i] = prev;
4441
4674
  else prev = type;
@@ -4446,7 +4679,7 @@ window.CodeMirror = (function() {
4446
4679
  // AL is found, change the type of the European number to Arabic
4447
4680
  // number.
4448
4681
  // W3. Change all ALs to R.
4449
- for (var i = 0, cur = startType; i < len; ++i) {
4682
+ for (var i = 0, cur = outerType; i < len; ++i) {
4450
4683
  var type = types[i];
4451
4684
  if (type == "1" && cur == "r") types[i] = "n";
4452
4685
  else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
@@ -4481,7 +4714,7 @@ window.CodeMirror = (function() {
4481
4714
  // W7. Search backwards from each instance of a European number
4482
4715
  // until the first strong type (R, L, or sor) is found. If an L is
4483
4716
  // found, then change the type of the European number to L.
4484
- for (var i = 0, cur = startType; i < len; ++i) {
4717
+ for (var i = 0, cur = outerType; i < len; ++i) {
4485
4718
  var type = types[i];
4486
4719
  if (cur == "L" && type == "1") types[i] = "L";
4487
4720
  else if (isStrong.test(type)) cur = type;
@@ -4496,8 +4729,8 @@ window.CodeMirror = (function() {
4496
4729
  for (var i = 0; i < len; ++i) {
4497
4730
  if (isNeutral.test(types[i])) {
4498
4731
  for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
4499
- var before = (i ? types[i-1] : startType) == "L";
4500
- var after = (end < len - 1 ? types[end] : startType) == "L";
4732
+ var before = (i ? types[i-1] : outerType) == "L";
4733
+ var after = (end < len - 1 ? types[end] : outerType) == "L";
4501
4734
  var replace = before || after ? "L" : "R";
4502
4735
  for (var j = i; j < end; ++j) types[j] = replace;
4503
4736
  i = end - 1;
@@ -4547,7 +4780,7 @@ window.CodeMirror = (function() {
4547
4780
 
4548
4781
  // THE END
4549
4782
 
4550
- CodeMirror.version = "3.0";
4783
+ CodeMirror.version = "3.02";
4551
4784
 
4552
4785
  return CodeMirror;
4553
4786
  })();