codemirror-rails 3.00 → 3.02

Sign up to get free protection for your applications and to get access to all the features.
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
  })();