##// END OF EJS Templates
Merge pull request #1327 from ellisonbg/updatecm2...
Fernando Perez -
r5977:237e5392 merge
parent child Browse files
Show More
@@ -17,3 +17,7 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 THE SOFTWARE.
19 THE SOFTWARE.
20
21 Please note that some subdirectories of the CodeMirror distribution
22 include their own LICENSE files, and are released under different
23 licences.
@@ -5,7 +5,7
5 We carry a mostly unmodified copy of CodeMirror. The current version we use
5 We carry a mostly unmodified copy of CodeMirror. The current version we use
6 is (*please update this information when updating versions*)::
6 is (*please update this information when updating versions*)::
7
7
8 CodeMirror 2.2
8 CodeMirror 7f93a5c
9
9
10 The only changes we've applied so far are these::
10 The only changes we've applied so far are these::
11
11
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -64,10 +64,13
64 visibility: visible;
64 visibility: visible;
65 }
65 }
66
66
67 span.CodeMirror-selected { background: #d9d9d9; }
67 div.CodeMirror-selected { background: #d9d9d9; }
68 .CodeMirror-focused span.CodeMirror-selected { background: #d2dcf8; }
68 .CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
69
69
70 .CodeMirror-searching {background: #ffa;}
70 .CodeMirror-searching {
71 background: #ffa;
72 background: rgba(255, 255, 0, .4);
73 }
71
74
72 /* Default theme */
75 /* Default theme */
73
76
@@ -1,5 +1,3
1 // CodeMirror version 2.2
2 //
3 // All functions that need access to the editor's state live inside
1 // All functions that need access to the editor's state live inside
4 // the CodeMirror function. Below that, at the bottom of the file,
2 // the CodeMirror function. Below that, at the bottom of the file,
5 // some utilities are defined.
3 // some utilities are defined.
@@ -22,17 +20,17 var CodeMirror = (function() {
22 // This mess creates the base DOM structure for the editor.
20 // This mess creates the base DOM structure for the editor.
23 wrapper.innerHTML =
21 wrapper.innerHTML =
24 '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
22 '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
25 '<textarea style="position: absolute; padding: 0; width: 1px;" wrap="off" ' +
23 '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
26 'autocorrect="off" autocapitalize="off"></textarea></div>' +
24 'autocorrect="off" autocapitalize="off"></textarea></div>' +
27 '<div class="CodeMirror-scroll" tabindex="-1">' +
25 '<div class="CodeMirror-scroll" tabindex="-1">' +
28 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
26 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
29 '<div style="position: relative">' + // Moved around its parent to cover visible view
27 '<div style="position: relative">' + // Moved around its parent to cover visible view
30 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
28 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
31 // Provides positioning relative to (visible) text origin
29 // Provides positioning relative to (visible) text origin
32 '<div class="CodeMirror-lines"><div style="position: relative">' +
30 '<div class="CodeMirror-lines"><div style="position: relative; z-index: 0">' +
33 '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
31 '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; outline: 5px auto none"></div>' +
34 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
32 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
35 '<div></div>' + // This DIV contains the actual code
33 '<div style="position: relative; z-index: -1"></div><div></div>' + // DIVs containing the selection and the actual code
36 '</div></div></div></div></div>';
34 '</div></div></div></div></div>';
37 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
35 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
38 // I've never seen more elegant code in my life.
36 // I've never seen more elegant code in my life.
@@ -40,11 +38,13 var CodeMirror = (function() {
40 scroller = wrapper.lastChild, code = scroller.firstChild,
38 scroller = wrapper.lastChild, code = scroller.firstChild,
41 mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
39 mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
42 lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
40 lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
43 cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
41 cursor = measure.nextSibling, selectionDiv = cursor.nextSibling,
42 lineDiv = selectionDiv.nextSibling;
44 themeChanged();
43 themeChanged();
45 // Needed to hide big blue blinking cursor on Mobile Safari
44 // Needed to hide big blue blinking cursor on Mobile Safari
46 if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
45 if (ios) input.style.width = "0px";
47 if (!webkit) lineSpace.draggable = true;
46 if (!webkit) lineSpace.draggable = true;
47 lineSpace.style.outline = "none";
48 if (options.tabindex != null) input.tabIndex = options.tabindex;
48 if (options.tabindex != null) input.tabIndex = options.tabindex;
49 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
49 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
50
50
@@ -71,7 +71,8 var CodeMirror = (function() {
71 var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
71 var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
72 // Selection-related flags. shiftSelecting obviously tracks
72 // Selection-related flags. shiftSelecting obviously tracks
73 // whether the user is holding shift.
73 // whether the user is holding shift.
74 var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
74 var shiftSelecting, lastClick, lastDoubleClick, lastScrollPos = 0, draggingText,
75 overwrite = false, suppressEdits = false;
75 // Variables used by startOperation/endOperation to track what
76 // Variables used by startOperation/endOperation to track what
76 // happened during the operation.
77 // happened during the operation.
77 var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
78 var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
@@ -99,6 +100,7 var CodeMirror = (function() {
99 // handled in onMouseDown for Gecko.
100 // handled in onMouseDown for Gecko.
100 if (!gecko) connect(scroller, "contextmenu", onContextMenu);
101 if (!gecko) connect(scroller, "contextmenu", onContextMenu);
101 connect(scroller, "scroll", function() {
102 connect(scroller, "scroll", function() {
103 lastScrollPos = scroller.scrollTop;
102 updateDisplay([]);
104 updateDisplay([]);
103 if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
105 if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
104 if (options.onScroll) options.onScroll(instance);
106 if (options.onScroll) options.onScroll(instance);
@@ -116,7 +118,9 var CodeMirror = (function() {
116 connect(scroller, "drop", operation(onDrop));
118 connect(scroller, "drop", operation(onDrop));
117 connect(scroller, "paste", function(){focusInput(); fastPoll();});
119 connect(scroller, "paste", function(){focusInput(); fastPoll();});
118 connect(input, "paste", fastPoll);
120 connect(input, "paste", fastPoll);
119 connect(input, "cut", operation(function(){replaceSelection("");}));
121 connect(input, "cut", operation(function(){
122 if (!options.readOnly) replaceSelection("");
123 }));
120
124
121 // IE throws unspecified error in certain cases, when
125 // IE throws unspecified error in certain cases, when
122 // trying to access activeElement before onload
126 // trying to access activeElement before onload
@@ -140,18 +144,23 var CodeMirror = (function() {
140 var oldVal = options[option];
144 var oldVal = options[option];
141 options[option] = value;
145 options[option] = value;
142 if (option == "mode" || option == "indentUnit") loadMode();
146 if (option == "mode" || option == "indentUnit") loadMode();
143 else if (option == "readOnly" && value) {onBlur(); input.blur();}
147 else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
148 else if (option == "readOnly" && !value) {resetInput(true);}
144 else if (option == "theme") themeChanged();
149 else if (option == "theme") themeChanged();
145 else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
150 else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
146 else if (option == "tabSize") operation(tabsChanged)();
151 else if (option == "tabSize") operation(tabsChanged)();
147 if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
152 if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
148 operation(gutterChanged)();
153 updateDisplay(true);
149 },
154 },
150 getOption: function(option) {return options[option];},
155 getOption: function(option) {return options[option];},
151 undo: operation(undo),
156 undo: operation(undo),
152 redo: operation(redo),
157 redo: operation(redo),
153 indentLine: operation(function(n, dir) {
158 indentLine: operation(function(n, dir) {
154 if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
159 if (typeof dir != "string") {
160 if (dir == null) dir = options.smartIndent ? "smart" : "prev";
161 else dir = dir ? "add" : "subtract";
162 }
163 if (isLine(n)) indentLine(n, dir);
155 }),
164 }),
156 indentSelection: operation(indentSelected),
165 indentSelection: operation(indentSelected),
157 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
166 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
@@ -268,9 +277,18 var CodeMirror = (function() {
268 });
277 });
269 return index;
278 return index;
270 },
279 },
280 scrollTo: function(x, y) {
281 if (x != null) scroller.scrollTop = x;
282 if (y != null) scroller.scrollLeft = y;
283 updateDisplay([]);
284 },
271
285
272 operation: function(f){return operation(f)();},
286 operation: function(f){return operation(f)();},
273 refresh: function(){updateDisplay(true);},
287 refresh: function(){
288 updateDisplay(true);
289 if (scroller.scrollHeight > lastScrollPos)
290 scroller.scrollTop = lastScrollPos;
291 },
274 getInputField: function(){return input;},
292 getInputField: function(){return input;},
275 getWrapperElement: function(){return wrapper;},
293 getWrapperElement: function(){return wrapper;},
276 getScrollerElement: function(){return scroller;},
294 getScrollerElement: function(){return scroller;},
@@ -297,7 +315,7 var CodeMirror = (function() {
297 }
315 }
298
316
299 function onMouseDown(e) {
317 function onMouseDown(e) {
300 setShift(e.shiftKey);
318 setShift(e_prop(e, "shiftKey"));
301 // Check whether this is a click in a widget
319 // Check whether this is a click in a widget
302 for (var n = e_target(e); n != wrapper; n = n.parentNode)
320 for (var n = e_target(e); n != wrapper; n = n.parentNode)
303 if (n.parentNode == code && n != mover) return;
321 if (n.parentNode == code && n != mover) return;
@@ -339,7 +357,7 var CodeMirror = (function() {
339 } else { lastClick = {time: now, pos: start}; }
357 } else { lastClick = {time: now, pos: start}; }
340
358
341 var last = start, going;
359 var last = start, going;
342 if (dragAndDrop && !posEq(sel.from, sel.to) &&
360 if (dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
343 !posLess(start, sel.from) && !posLess(sel.to, start)) {
361 !posLess(start, sel.from) && !posLess(sel.to, start)) {
344 // Let the drag handler handle this.
362 // Let the drag handler handle this.
345 if (webkit) lineSpace.draggable = true;
363 if (webkit) lineSpace.draggable = true;
@@ -440,15 +458,19 var CodeMirror = (function() {
440 e.dataTransfer.setData("Text", txt);
458 e.dataTransfer.setData("Text", txt);
441 }
459 }
442 function handleKeyBinding(e) {
460 function handleKeyBinding(e) {
443 var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
461 var name = keyNames[e_prop(e, "keyCode")], next = keyMap[options.keyMap].auto, bound, dropShift;
462 function handleNext() {
463 return next.call ? next.call(null, instance) : next;
464 }
444 if (name == null || e.altGraphKey) {
465 if (name == null || e.altGraphKey) {
445 if (next) options.keyMap = next;
466 if (next) options.keyMap = handleNext();
446 return null;
467 return null;
447 }
468 }
448 if (e.altKey) name = "Alt-" + name;
469 if (e_prop(e, "altKey")) name = "Alt-" + name;
449 if (e.ctrlKey) name = "Ctrl-" + name;
470 if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
450 if (e.metaKey) name = "Cmd-" + name;
471 if (e_prop(e, "metaKey")) name = "Cmd-" + name;
451 if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
472 if (e_prop(e, "shiftKey") &&
473 (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
452 dropShift = true;
474 dropShift = true;
453 } else {
475 } else {
454 bound = lookupKey(name, options.extraKeys, options.keyMap);
476 bound = lookupKey(name, options.extraKeys, options.keyMap);
@@ -457,40 +479,44 var CodeMirror = (function() {
457 if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
479 if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
458 else bound = null;
480 else bound = null;
459 }
481 }
460 if (next && (bound || !isModifierKey(e))) options.keyMap = next;
482 if (next && (bound || !isModifierKey(e))) options.keyMap = handleNext();
461 if (!bound) return false;
483 if (!bound) return false;
462 if (dropShift) {
484 var prevShift = shiftSelecting;
463 var prevShift = shiftSelecting;
485 try {
464 shiftSelecting = null;
486 if (options.readOnly) suppressEdits = true;
487 if (dropShift) shiftSelecting = null;
465 bound(instance);
488 bound(instance);
489 } finally {
466 shiftSelecting = prevShift;
490 shiftSelecting = prevShift;
467 } else bound(instance);
491 suppressEdits = false;
492 }
468 e_preventDefault(e);
493 e_preventDefault(e);
469 return true;
494 return true;
470 }
495 }
471 var lastStoppedKey = null;
496 var lastStoppedKey = null;
472 function onKeyDown(e) {
497 function onKeyDown(e) {
473 if (!focused) onFocus();
498 if (!focused) onFocus();
474 var code = e.keyCode;
499 if (ie && e.keyCode == 27) { e.returnValue = false; }
500 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
501 var code = e_prop(e, "keyCode");
475 // IE does strange things with escape.
502 // IE does strange things with escape.
476 if (ie && code == 27) { e.returnValue = false; }
503 setShift(code == 16 || e_prop(e, "shiftKey"));
477 setShift(code == 16 || e.shiftKey);
478 // First give onKeyEvent option a chance to handle this.
504 // First give onKeyEvent option a chance to handle this.
479 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
480 var handled = handleKeyBinding(e);
505 var handled = handleKeyBinding(e);
481 if (window.opera) {
506 if (window.opera) {
482 lastStoppedKey = handled ? e.keyCode : null;
507 lastStoppedKey = handled ? code : null;
483 // Opera has no cut event... we try to at least catch the key combo
508 // Opera has no cut event... we try to at least catch the key combo
484 if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
509 if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
485 replaceSelection("");
510 replaceSelection("");
486 }
511 }
487 }
512 }
488 function onKeyPress(e) {
513 function onKeyPress(e) {
489 if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
514 var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
515 if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
490 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
516 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
491 if (window.opera && !e.which && handleKeyBinding(e)) return;
517 if (window.opera && !e.which && handleKeyBinding(e)) return;
492 if (options.electricChars && mode.electricChars) {
518 if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
493 var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
519 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
494 if (mode.electricChars.indexOf(ch) > -1)
520 if (mode.electricChars.indexOf(ch) > -1)
495 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
521 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
496 }
522 }
@@ -498,11 +524,11 var CodeMirror = (function() {
498 }
524 }
499 function onKeyUp(e) {
525 function onKeyUp(e) {
500 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
526 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
501 if (e.keyCode == 16) shiftSelecting = null;
527 if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
502 }
528 }
503
529
504 function onFocus() {
530 function onFocus() {
505 if (options.readOnly) return;
531 if (options.readOnly == "nocursor") return;
506 if (!focused) {
532 if (!focused) {
507 if (options.onFocus) options.onFocus(instance);
533 if (options.onFocus) options.onFocus(instance);
508 focused = true;
534 focused = true;
@@ -517,6 +543,10 var CodeMirror = (function() {
517 if (focused) {
543 if (focused) {
518 if (options.onBlur) options.onBlur(instance);
544 if (options.onBlur) options.onBlur(instance);
519 focused = false;
545 focused = false;
546 if (bracketHighlighted)
547 operation(function(){
548 if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
549 })();
520 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
550 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
521 }
551 }
522 clearInterval(blinker);
552 clearInterval(blinker);
@@ -526,6 +556,7 var CodeMirror = (function() {
526 // Replace the range from from to to by the strings in newText.
556 // Replace the range from from to to by the strings in newText.
527 // Afterwards, set the selection to selFrom, selTo.
557 // Afterwards, set the selection to selFrom, selTo.
528 function updateLines(from, to, newText, selFrom, selTo) {
558 function updateLines(from, to, newText, selFrom, selTo) {
559 if (suppressEdits) return;
529 if (history) {
560 if (history) {
530 var old = [];
561 var old = [];
531 doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
562 doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
@@ -550,6 +581,7 var CodeMirror = (function() {
550 function redo() {unredoHelper(history.undone, history.done);}
581 function redo() {unredoHelper(history.undone, history.done);}
551
582
552 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
583 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
584 if (suppressEdits) return;
553 var recomputeMaxLength = false, maxLineLength = maxLine.length;
585 var recomputeMaxLength = false, maxLineLength = maxLine.length;
554 if (!options.lineWrapping)
586 if (!options.lineWrapping)
555 doc.iter(from.line, to.line, function(line) {
587 doc.iter(from.line, to.line, function(line) {
@@ -651,7 +683,8 var CodeMirror = (function() {
651 setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
683 setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
652
684
653 // Make sure the scroll-size div has the correct height.
685 // Make sure the scroll-size div has the correct height.
654 code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
686 if (scroller.clientHeight)
687 code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
655 }
688 }
656
689
657 function replaceRange(code, from, to) {
690 function replaceRange(code, from, to) {
@@ -729,7 +762,7 var CodeMirror = (function() {
729 // supported or compatible enough yet to rely on.)
762 // supported or compatible enough yet to rely on.)
730 var prevInput = "";
763 var prevInput = "";
731 function readInput() {
764 function readInput() {
732 if (leaveInputAlone || !focused || hasSelection(input)) return false;
765 if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
733 var text = input.value;
766 var text = input.value;
734 if (text == prevInput) return false;
767 if (text == prevInput) return false;
735 shiftSelecting = null;
768 shiftSelecting = null;
@@ -752,7 +785,7 var CodeMirror = (function() {
752 }
785 }
753
786
754 function focusInput() {
787 function focusInput() {
755 if (!options.readOnly) input.focus();
788 if (options.readOnly != "nocursor") input.focus();
756 }
789 }
757
790
758 function scrollEditorIntoView() {
791 function scrollEditorIntoView() {
@@ -809,7 +842,7 var CodeMirror = (function() {
809 // Compute the new visible window
842 // Compute the new visible window
810 var visible = visibleLines();
843 var visible = visibleLines();
811 // Bail out if the visible area is already rendered and nothing changed.
844 // Bail out if the visible area is already rendered and nothing changed.
812 if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
845 if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) return;
813 var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
846 var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
814 if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
847 if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
815 if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
848 if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
@@ -831,9 +864,9 var CodeMirror = (function() {
831 intact.sort(function(a, b) {return a.domStart - b.domStart;});
864 intact.sort(function(a, b) {return a.domStart - b.domStart;});
832
865
833 var th = textHeight(), gutterDisplay = gutter.style.display;
866 var th = textHeight(), gutterDisplay = gutter.style.display;
834 lineDiv.style.display = gutter.style.display = "none";
867 lineDiv.style.display = "none";
835 patchDisplay(from, to, intact);
868 patchDisplay(from, to, intact);
836 lineDiv.style.display = "";
869 lineDiv.style.display = gutter.style.display = "";
837
870
838 // Position the mover div to align with the lines it's supposed
871 // Position the mover div to align with the lines it's supposed
839 // to be showing (which will cover the visible display)
872 // to be showing (which will cover the visible display)
@@ -844,7 +877,8 var CodeMirror = (function() {
844 showingFrom = from; showingTo = to;
877 showingFrom = from; showingTo = to;
845 displayOffset = heightAtLine(doc, from);
878 displayOffset = heightAtLine(doc, from);
846 mover.style.top = (displayOffset * th) + "px";
879 mover.style.top = (displayOffset * th) + "px";
847 code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
880 if (scroller.clientHeight)
881 code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
848
882
849 // Since this is all rather error prone, it is honoured with the
883 // Since this is all rather error prone, it is honoured with the
850 // only assertion in the whole file.
884 // only assertion in the whole file.
@@ -854,14 +888,19 var CodeMirror = (function() {
854
888
855 if (options.lineWrapping) {
889 if (options.lineWrapping) {
856 maxWidth = scroller.clientWidth;
890 maxWidth = scroller.clientWidth;
857 var curNode = lineDiv.firstChild;
891 var curNode = lineDiv.firstChild, heightChanged = false;
858 doc.iter(showingFrom, showingTo, function(line) {
892 doc.iter(showingFrom, showingTo, function(line) {
859 if (!line.hidden) {
893 if (!line.hidden) {
860 var height = Math.round(curNode.offsetHeight / th) || 1;
894 var height = Math.round(curNode.offsetHeight / th) || 1;
861 if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
895 if (line.height != height) {
896 updateLineHeight(line, height);
897 gutterDirty = heightChanged = true;
898 }
862 }
899 }
863 curNode = curNode.nextSibling;
900 curNode = curNode.nextSibling;
864 });
901 });
902 if (heightChanged)
903 code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
865 } else {
904 } else {
866 if (maxWidth == null) maxWidth = stringWidth(maxLine);
905 if (maxWidth == null) maxWidth = stringWidth(maxLine);
867 if (maxWidth > scroller.clientWidth) {
906 if (maxWidth > scroller.clientWidth) {
@@ -875,7 +914,7 var CodeMirror = (function() {
875 }
914 }
876 gutter.style.display = gutterDisplay;
915 gutter.style.display = gutterDisplay;
877 if (different || gutterDirty) updateGutter();
916 if (different || gutterDirty) updateGutter();
878 updateCursor();
917 updateSelection();
879 if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
918 if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
880 return true;
919 return true;
881 }
920 }
@@ -922,21 +961,19 var CodeMirror = (function() {
922 }
961 }
923 // This pass fills in the lines that actually changed.
962 // This pass fills in the lines that actually changed.
924 var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
963 var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
925 var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
926 var scratch = targetDocument.createElement("div"), newElt;
964 var scratch = targetDocument.createElement("div"), newElt;
927 doc.iter(from, to, function(line) {
965 doc.iter(from, to, function(line) {
928 var ch1 = null, ch2 = null;
929 if (inSel) {
930 ch1 = 0;
931 if (sto == j) {inSel = false; ch2 = sel.to.ch;}
932 } else if (sfrom == j) {
933 if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
934 else {inSel = true; ch1 = sel.from.ch;}
935 }
936 if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
966 if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
937 if (!nextIntact || nextIntact.from > j) {
967 if (!nextIntact || nextIntact.from > j) {
938 if (line.hidden) scratch.innerHTML = "<pre></pre>";
968 if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
939 else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
969 else {
970 var html = '<pre>' + line.getHTML(tabText) + '</pre>';
971 // Kludge to make sure the styled element lies behind the selection (by z-index)
972 if (line.className)
973 html = '<div style="position: relative"><pre class="' + line.className +
974 '" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>' + html + "</div>";
975 }
976 scratch.innerHTML = html;
940 lineDiv.insertBefore(scratch.firstChild, curNode);
977 lineDiv.insertBefore(scratch.firstChild, curNode);
941 } else {
978 } else {
942 curNode = curNode.nextSibling;
979 curNode = curNode.nextSibling;
@@ -975,18 +1012,39 var CodeMirror = (function() {
975 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
1012 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
976 gutterDirty = false;
1013 gutterDirty = false;
977 }
1014 }
978 function updateCursor() {
1015 function updateSelection() {
979 var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
1016 var collapsed = posEq(sel.from, sel.to);
980 var pos = localCoords(head, true);
1017 var fromPos = localCoords(sel.from, true);
1018 var toPos = collapsed ? fromPos : localCoords(sel.to, true);
1019 var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
981 var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
1020 var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
982 inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
1021 inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
983 inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
1022 inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
984 if (posEq(sel.from, sel.to)) {
1023 if (collapsed) {
985 cursor.style.top = pos.y + "px";
1024 cursor.style.top = headPos.y + "px";
986 cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
1025 cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
987 cursor.style.display = "";
1026 cursor.style.display = "";
1027 selectionDiv.style.display = "none";
1028 } else {
1029 var sameLine = fromPos.y == toPos.y, html = "";
1030 function add(left, top, right, height) {
1031 html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
1032 'px; top: ' + top + 'px; right: ' + right + 'px; height: ' + height + 'px"></div>';
1033 }
1034 if (sel.from.ch && fromPos.y >= 0) {
1035 var right = sameLine ? lineSpace.clientWidth - toPos.x : 0;
1036 add(fromPos.x, fromPos.y, right, th);
1037 }
1038 var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
1039 var middleHeight = Math.min(toPos.y, lineSpace.clientHeight) - middleStart;
1040 if (middleHeight > 0.2 * th)
1041 add(0, middleStart, 0, middleHeight);
1042 if ((!sameLine || !sel.from.ch) && toPos.y < lineSpace.clientHeight - .5 * th)
1043 add(0, toPos.y, lineSpace.clientWidth - toPos.x, th);
1044 selectionDiv.innerHTML = html;
1045 cursor.style.display = "none";
1046 selectionDiv.style.display = "";
988 }
1047 }
989 else cursor.style.display = "none";
990 }
1048 }
991
1049
992 function setShift(val) {
1050 function setShift(val) {
@@ -1019,30 +1077,6 var CodeMirror = (function() {
1019 else if (posEq(from, sel.to)) sel.inverted = false;
1077 else if (posEq(from, sel.to)) sel.inverted = false;
1020 else if (posEq(to, sel.from)) sel.inverted = true;
1078 else if (posEq(to, sel.from)) sel.inverted = true;
1021
1079
1022 // Some ugly logic used to only mark the lines that actually did
1023 // see a change in selection as changed, rather than the whole
1024 // selected range.
1025 if (posEq(from, to)) {
1026 if (!posEq(sel.from, sel.to))
1027 changes.push({from: oldFrom, to: oldTo + 1});
1028 }
1029 else if (posEq(sel.from, sel.to)) {
1030 changes.push({from: from.line, to: to.line + 1});
1031 }
1032 else {
1033 if (!posEq(from, sel.from)) {
1034 if (from.line < oldFrom)
1035 changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
1036 else
1037 changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
1038 }
1039 if (!posEq(to, sel.to)) {
1040 if (to.line < oldTo)
1041 changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
1042 else
1043 changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
1044 }
1045 }
1046 sel.from = from; sel.to = to;
1080 sel.from = from; sel.to = to;
1047 selectionChanged = true;
1081 selectionChanged = true;
1048 }
1082 }
@@ -1123,7 +1157,7 var CodeMirror = (function() {
1123 function moveV(dir, unit) {
1157 function moveV(dir, unit) {
1124 var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1158 var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1125 if (goalColumn != null) pos.x = goalColumn;
1159 if (goalColumn != null) pos.x = goalColumn;
1126 if (unit == "page") dist = scroller.clientHeight;
1160 if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1127 else if (unit == "line") dist = textHeight();
1161 else if (unit == "line") dist = textHeight();
1128 var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1162 var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1129 setCursor(target.line, target.ch, true);
1163 setCursor(target.line, target.ch, true);
@@ -1316,9 +1350,12 var CodeMirror = (function() {
1316 if (line.hidden != hidden) {
1350 if (line.hidden != hidden) {
1317 line.hidden = hidden;
1351 line.hidden = hidden;
1318 updateLineHeight(line, hidden ? 0 : 1);
1352 updateLineHeight(line, hidden ? 0 : 1);
1319 if (hidden && (sel.from.line == no || sel.to.line == no))
1353 var fline = sel.from.line, tline = sel.to.line;
1320 setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
1354 if (hidden && (fline == no || tline == no)) {
1321 skipHidden(sel.to, sel.to.line, sel.to.ch));
1355 var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
1356 var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
1357 setSelection(from, to);
1358 }
1322 return (gutterDirty = true);
1359 return (gutterDirty = true);
1323 }
1360 }
1324 });
1361 });
@@ -1351,7 +1388,7 var CodeMirror = (function() {
1351 if (x <= 0) return 0;
1388 if (x <= 0) return 0;
1352 var lineObj = getLine(line), text = lineObj.text;
1389 var lineObj = getLine(line), text = lineObj.text;
1353 function getX(len) {
1390 function getX(len) {
1354 measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, tabText, len) + "</span></pre>";
1391 measure.innerHTML = "<pre><span>" + lineObj.getHTML(tabText, len) + "</span></pre>";
1355 return measure.firstChild.firstChild.offsetWidth;
1392 return measure.firstChild.firstChild.offsetWidth;
1356 }
1393 }
1357 var from = 0, fromX = 0, to = text.length, toX;
1394 var from = 0, fromX = 0, to = text.length, toX;
@@ -1377,19 +1414,20 var CodeMirror = (function() {
1377
1414
1378 var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1415 var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1379 function measureLine(line, ch) {
1416 function measureLine(line, ch) {
1417 if (ch == 0) return {top: 0, left: 0};
1380 var extra = "";
1418 var extra = "";
1381 // Include extra text at the end to make sure the measured line is wrapped in the right way.
1419 // Include extra text at the end to make sure the measured line is wrapped in the right way.
1382 if (options.lineWrapping) {
1420 if (options.lineWrapping) {
1383 var end = line.text.indexOf(" ", ch + 2);
1421 var end = line.text.indexOf(" ", ch + 2);
1384 extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1422 extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1385 }
1423 }
1386 measure.innerHTML = "<pre>" + line.getHTML(null, null, false, tabText, ch) +
1424 measure.innerHTML = "<pre>" + line.getHTML(tabText, ch) +
1387 '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1425 '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1388 extra + "</pre>";
1426 extra + "</pre>";
1389 var elt = document.getElementById("CodeMirror-temp-" + tempId);
1427 var elt = document.getElementById("CodeMirror-temp-" + tempId);
1390 var top = elt.offsetTop, left = elt.offsetLeft;
1428 var top = elt.offsetTop, left = elt.offsetLeft;
1391 // Older IEs report zero offsets for spans directly after a wrap
1429 // Older IEs report zero offsets for spans directly after a wrap
1392 if (ie && ch && top == 0 && left == 0) {
1430 if (ie && top == 0 && left == 0) {
1393 var backup = document.createElement("span");
1431 var backup = document.createElement("span");
1394 backup.innerHTML = "x";
1432 backup.innerHTML = "x";
1395 elt.parentNode.insertBefore(backup, elt.nextSibling);
1433 elt.parentNode.insertBefore(backup, elt.nextSibling);
@@ -1667,7 +1705,7 var CodeMirror = (function() {
1667 if (selectionChanged) reScroll = !scrollCursorIntoView();
1705 if (selectionChanged) reScroll = !scrollCursorIntoView();
1668 if (changes.length) updated = updateDisplay(changes, true);
1706 if (changes.length) updated = updateDisplay(changes, true);
1669 else {
1707 else {
1670 if (selectionChanged) updateCursor();
1708 if (selectionChanged) updateSelection();
1671 if (gutterDirty) updateGutter();
1709 if (gutterDirty) updateGutter();
1672 }
1710 }
1673 if (reScroll) scrollCursorIntoView();
1711 if (reScroll) scrollCursorIntoView();
@@ -1714,6 +1752,7 var CodeMirror = (function() {
1714 theme: "default",
1752 theme: "default",
1715 indentUnit: 2,
1753 indentUnit: 2,
1716 indentWithTabs: false,
1754 indentWithTabs: false,
1755 smartIndent: true,
1717 tabSize: 4,
1756 tabSize: 4,
1718 keyMap: "default",
1757 keyMap: "default",
1719 extraKeys: null,
1758 extraKeys: null,
@@ -1740,7 +1779,8 var CodeMirror = (function() {
1740 document: window.document
1779 document: window.document
1741 };
1780 };
1742
1781
1743 var mac = /Mac/.test(navigator.platform);
1782 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
1783 var mac = ios || /Mac/.test(navigator.platform);
1744 var win = /Win/.test(navigator.platform);
1784 var win = /Win/.test(navigator.platform);
1745
1785
1746 // Known modes, by name and by MIME
1786 // Known modes, by name and by MIME
@@ -1883,7 +1923,7 var CodeMirror = (function() {
1883 return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
1923 return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
1884 }
1924 }
1885 function isModifierKey(event) {
1925 function isModifierKey(event) {
1886 var name = keyNames[event.keyCode];
1926 var name = keyNames[e_prop(event, "keyCode")];
1887 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1927 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1888 }
1928 }
1889
1929
@@ -2100,7 +2140,8 var CodeMirror = (function() {
2100 this.stateAfter = null;
2140 this.stateAfter = null;
2101 if (mk) {
2141 if (mk) {
2102 var diff = text.length - (to - from);
2142 var diff = text.length - (to - from);
2103 for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
2143 for (var i = 0; i < mk.length; ++i) {
2144 var mark = mk[i];
2104 mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2145 mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2105 if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2146 if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2106 }
2147 }
@@ -2227,10 +2268,8 var CodeMirror = (function() {
2227 indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2268 indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2228 // Produces an HTML fragment for the line, taking selection,
2269 // Produces an HTML fragment for the line, taking selection,
2229 // marking, and highlighting into account.
2270 // marking, and highlighting into account.
2230 getHTML: function(sfrom, sto, includePre, tabText, endAt) {
2271 getHTML: function(tabText, endAt) {
2231 var html = [], first = true;
2272 var html = [], first = true;
2232 if (includePre)
2233 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
2234 function span(text, style) {
2273 function span(text, style) {
2235 if (!text) return;
2274 if (!text) return;
2236 // Work around a bug where, in some compat modes, IE ignores leading spaces
2275 // Work around a bug where, in some compat modes, IE ignores leading spaces
@@ -2240,60 +2279,57 var CodeMirror = (function() {
2240 else html.push(htmlEscape(text).replace(/\t/g, tabText));
2279 else html.push(htmlEscape(text).replace(/\t/g, tabText));
2241 }
2280 }
2242 var st = this.styles, allText = this.text, marked = this.marked;
2281 var st = this.styles, allText = this.text, marked = this.marked;
2243 if (sfrom == sto) sfrom = null;
2244 var len = allText.length;
2282 var len = allText.length;
2245 if (endAt != null) len = Math.min(endAt, len);
2283 if (endAt != null) len = Math.min(endAt, len);
2284 function styleToClass(style) {
2285 if (!style) return null;
2286 return "cm-" + style.replace(/ +/g, " cm-");
2287 }
2246
2288
2247 if (!allText && endAt == null)
2289 if (!allText && endAt == null)
2248 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
2290 span(" ");
2249 else if (!marked && sfrom == null)
2291 else if (!marked || !marked.length)
2250 for (var i = 0, ch = 0; ch < len; i+=2) {
2292 for (var i = 0, ch = 0; ch < len; i+=2) {
2251 var str = st[i], style = st[i+1], l = str.length;
2293 var str = st[i], style = st[i+1], l = str.length;
2252 if (ch + l > len) str = str.slice(0, len - ch);
2294 if (ch + l > len) str = str.slice(0, len - ch);
2253 ch += l;
2295 ch += l;
2254 span(str, style && "cm-" + style);
2296 span(str, styleToClass(style));
2255 }
2297 }
2256 else {
2298 else {
2257 var pos = 0, i = 0, text = "", style, sg = 0;
2299 var pos = 0, i = 0, text = "", style, sg = 0;
2258 var markpos = -1, mark = null;
2300 var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2259 function nextMark() {
2301 function advanceMarks() {
2260 if (marked) {
2302 var m;
2261 markpos += 1;
2303 while (markpos < marked.length &&
2262 mark = (markpos < marked.length) ? marked[markpos] : null;
2304 ((m = marked[markpos]).from == pos || m.from == null)) {
2305 if (m.style != null) marks.push(m);
2306 ++markpos;
2307 }
2308 nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2309 for (var i = 0; i < marks.length; ++i) {
2310 var to = marks[i].to || Infinity;
2311 if (to == pos) marks.splice(i--, 1);
2312 else nextChange = Math.min(to, nextChange);
2263 }
2313 }
2264 }
2314 }
2265 nextMark();
2315 var m = 0;
2266 while (pos < len) {
2316 while (pos < len) {
2267 var upto = len;
2317 if (nextChange == pos) advanceMarks();
2268 var extraStyle = "";
2318 var upto = Math.min(len, nextChange);
2269 if (sfrom != null) {
2319 while (true) {
2270 if (sfrom > pos) upto = sfrom;
2320 if (text) {
2271 else if (sto == null || sto > pos) {
2321 var end = pos + text.length;
2272 extraStyle = " CodeMirror-selected";
2322 var appliedStyle = style;
2273 if (sto != null) upto = Math.min(upto, sto);
2323 for (var j = 0; j < marks.length; ++j)
2274 }
2324 appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2275 }
2325 span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2276 while (mark && mark.to != null && mark.to <= pos) nextMark();
2326 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2277 if (mark) {
2327 pos = end;
2278 if (mark.from > pos) upto = Math.min(upto, mark.from);
2279 else {
2280 extraStyle += " " + mark.style;
2281 if (mark.to != null) upto = Math.min(upto, mark.to);
2282 }
2328 }
2283 }
2329 text = st[i++]; style = styleToClass(st[i++]);
2284 for (;;) {
2285 var end = pos + text.length;
2286 var appliedStyle = style;
2287 if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
2288 span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2289 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2290 pos = end;
2291 text = st[i++]; style = "cm-" + st[i++];
2292 }
2330 }
2293 }
2331 }
2294 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
2295 }
2332 }
2296 if (includePre) html.push("</pre>");
2297 return html.join("");
2333 return html.join("");
2298 },
2334 },
2299 cleanUp: function() {
2335 cleanUp: function() {
@@ -2384,6 +2420,7 var CodeMirror = (function() {
2384 var lines = [];
2420 var lines = [];
2385 this.collapse(lines);
2421 this.collapse(lines);
2386 this.children = [new LeafChunk(lines)];
2422 this.children = [new LeafChunk(lines)];
2423 this.children[0].parent = this;
2387 }
2424 }
2388 },
2425 },
2389 collapse: function(lines) {
2426 collapse: function(lines) {
@@ -2566,6 +2603,13 var CodeMirror = (function() {
2566 else if (e.button & 4) return 2;
2603 else if (e.button & 4) return 2;
2567 }
2604 }
2568
2605
2606 // Allow 3rd-party code to override event properties by adding an override
2607 // object to an event object.
2608 function e_prop(e, prop) {
2609 var overridden = e.override && e.override.hasOwnProperty(prop);
2610 return overridden ? e.override[prop] : e[prop];
2611 }
2612
2569 // Event handler registration. If disconnect is true, it'll return a
2613 // Event handler registration. If disconnect is true, it'll return a
2570 // function that unregisters the handler.
2614 // function that unregisters the handler.
2571 function connect(node, type, handler, disconnect) {
2615 function connect(node, type, handler, disconnect) {
@@ -2665,6 +2709,12 var CodeMirror = (function() {
2665 function eltText(node) {
2709 function eltText(node) {
2666 return node.textContent || node.innerText || node.nodeValue || "";
2710 return node.textContent || node.innerText || node.nodeValue || "";
2667 }
2711 }
2712 function selectInput(node) {
2713 if (ios) { // Mobile Safari apparently has a bug where select() is broken.
2714 node.selectionStart = 0;
2715 node.selectionEnd = node.value.length;
2716 } else node.select();
2717 }
2668
2718
2669 // Operations on {line, ch} objects.
2719 // Operations on {line, ch} objects.
2670 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2720 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
@@ -2695,7 +2745,7 var CodeMirror = (function() {
2695 // Used to position the cursor after an undo/redo by finding the
2745 // Used to position the cursor after an undo/redo by finding the
2696 // last edited character.
2746 // last edited character.
2697 function editEnd(from, to) {
2747 function editEnd(from, to) {
2698 if (!to) return from ? from.length : 0;
2748 if (!to) return 0;
2699 if (!from) return to.length;
2749 if (!from) return to.length;
2700 for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2750 for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2701 if (from.charAt(i) != to.charAt(j)) break;
2751 if (from.charAt(i) != to.charAt(j)) break;
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -1,3 +1,110
1 // the tagRangeFinder function is
2 // Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
3 // released under the MIT license (../../LICENSE) like the rest of CodeMirror
4 CodeMirror.tagRangeFinder = function(cm, line) {
5 var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
6 var nameChar = nameStartChar + "\-\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
7 var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
8
9 var lineText = cm.getLine(line);
10 var found = false;
11 var tag = null;
12 var pos = 0;
13 while (!found) {
14 pos = lineText.indexOf("<", pos);
15 if (-1 == pos) // no tag on line
16 return;
17 if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
18 pos++;
19 continue;
20 }
21 // ok we weem to have a start tag
22 if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
23 pos++;
24 continue;
25 }
26 var gtPos = lineText.indexOf(">", pos + 1);
27 if (-1 == gtPos) { // end of start tag not in line
28 var l = line + 1;
29 var foundGt = false;
30 var lastLine = cm.lineCount();
31 while (l < lastLine && !foundGt) {
32 var lt = cm.getLine(l);
33 var gt = lt.indexOf(">");
34 if (-1 != gt) { // found a >
35 foundGt = true;
36 var slash = lt.lastIndexOf("/", gt);
37 if (-1 != slash && slash < gt) {
38 var str = lineText.substr(slash, gt - slash + 1);
39 if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
40 return l+1;
41 }
42 }
43 l++;
44 }
45 found = true;
46 }
47 else {
48 var slashPos = lineText.lastIndexOf("/", gtPos);
49 if (-1 == slashPos) { // cannot be empty tag
50 found = true;
51 // don't continue
52 }
53 else { // empty tag?
54 // check if really empty tag
55 var str = lineText.substr(slashPos, gtPos - slashPos + 1);
56 if (!str.match( /\/\s*\>/ )) { // finally not empty
57 found = true;
58 // don't continue
59 }
60 }
61 }
62 if (found) {
63 var subLine = lineText.substr(pos + 1);
64 tag = subLine.match(xmlNAMERegExp);
65 if (tag) {
66 // we have an element name, wooohooo !
67 tag = tag[0];
68 // do we have the close tag on same line ???
69 if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
70 {
71 found = false;
72 }
73 // we don't, so we have a candidate...
74 }
75 else
76 found = false;
77 }
78 if (!found)
79 pos++;
80 }
81
82 if (found) {
83 var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\s)|(\\<" + tag + "$)";
84 var startTagRegExp = new RegExp(startTag, "g");
85 var endTag = "</" + tag + ">";
86 var depth = 1;
87 var l = line + 1;
88 var lastLine = cm.lineCount();
89 while (l < lastLine) {
90 lineText = cm.getLine(l);
91 var match = lineText.match(startTagRegExp);
92 if (match) {
93 for (var i = 0; i < match.length; i++) {
94 if (match[i] == endTag)
95 depth--;
96 else
97 depth++;
98 if (!depth)
99 return l+1;
100 }
101 }
102 l++;
103 }
104 return;
105 }
106 };
107
1 CodeMirror.braceRangeFinder = function(cm, line) {
108 CodeMirror.braceRangeFinder = function(cm, line) {
2 var lineText = cm.getLine(line);
109 var lineText = cm.getLine(line);
3 var startChar = lineText.lastIndexOf("{");
110 var startChar = lineText.lastIndexOf("{");
@@ -50,7 +50,7 CodeMirror.defineExtension("autoIndentRange", function (from, to) {
50 var cmInstance = this;
50 var cmInstance = this;
51 this.operation(function () {
51 this.operation(function () {
52 for (var i = from.line; i <= to.line; i++) {
52 for (var i = from.line; i <= to.line; i++) {
53 cmInstance.indentLine(i);
53 cmInstance.indentLine(i, "smart");
54 }
54 }
55 });
55 });
56 });
56 });
@@ -70,7 +70,7 CodeMirror.defineExtension("autoFormatRange", function (from, to) {
70 var startLine = cmInstance.posFromIndex(absStart).line;
70 var startLine = cmInstance.posFromIndex(absStart).line;
71 var endLine = cmInstance.posFromIndex(absStart + res.length).line;
71 var endLine = cmInstance.posFromIndex(absStart + res.length).line;
72 for (var i = startLine; i <= endLine; i++) {
72 for (var i = startLine; i <= endLine; i++) {
73 cmInstance.indentLine(i);
73 cmInstance.indentLine(i, "smart");
74 }
74 }
75 });
75 });
76 });
76 });
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -69,7 +69,7 CodeMirror.defineMode("javascript", function(config, parserConfig) {
69 else if (state.reAllowed) {
69 else if (state.reAllowed) {
70 nextUntilUnescaped(stream, "/");
70 nextUntilUnescaped(stream, "/");
71 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
71 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
72 return ret("regexp", "string");
72 return ret("regexp", "string-2");
73 }
73 }
74 else {
74 else {
75 stream.eatWhile(isOperatorChar);
75 stream.eatWhile(isOperatorChar);
@@ -230,7 +230,7 CodeMirror.defineMode("javascript", function(config, parserConfig) {
230 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
230 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
231 if (type == "function") return cont(functiondef);
231 if (type == "function") return cont(functiondef);
232 if (type == "keyword c") return cont(maybeexpression);
232 if (type == "keyword c") return cont(maybeexpression);
233 if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
233 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
234 if (type == "operator") return cont(expression);
234 if (type == "operator") return cont(expression);
235 if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
235 if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
236 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
236 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -18,28 +18,35 CodeMirror.defineMode("python", function(conf, parserConf) {
18 'for', 'from', 'global', 'if', 'import',
18 'for', 'from', 'global', 'if', 'import',
19 'lambda', 'pass', 'raise', 'return',
19 'lambda', 'pass', 'raise', 'return',
20 'try', 'while', 'with', 'yield'];
20 'try', 'while', 'with', 'yield'];
21 var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate',
21 var commonBuiltins = ['abs', 'all', 'any', 'bin', 'bool', 'bytearray', 'callable', 'chr',
22 'float', 'frozenset', 'int', 'list', 'object',
22 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
23 'property', 'reversed', 'set', 'slice', 'staticmethod',
23 'enumerate', 'eval', 'filter', 'float', 'format', 'frozenset',
24 'str', 'super', 'tuple', 'type'];
24 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id',
25 var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode',
25 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len',
26 'xrange'],
26 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
27 'object', 'oct', 'open', 'ord', 'pow', 'property', 'range',
28 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
29 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple',
30 'type', 'vars', 'zip', '__import__', 'NotImplemented',
31 'Ellipsis', '__debug__'];
32 var py2 = {'builtins': ['apply', 'basestring', 'buffer', 'cmp', 'coerce', 'execfile',
33 'file', 'intern', 'long', 'raw_input', 'reduce', 'reload',
34 'unichr', 'unicode', 'xrange', 'False', 'True', 'None'],
27 'keywords': ['exec', 'print']};
35 'keywords': ['exec', 'print']};
28 var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview',
36 var py3 = {'builtins': ['ascii', 'bytes', 'exec', 'print'],
29 'open', 'range', 'zip'],
37 'keywords': ['nonlocal', 'False', 'True', 'None']};
30 'keywords': ['nonlocal']};
31
38
32 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
39 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
33 commonkeywords = commonkeywords.concat(py3.keywords);
40 commonkeywords = commonkeywords.concat(py3.keywords);
34 commontypes = commontypes.concat(py3.types);
41 commonBuiltins = commonBuiltins.concat(py3.builtins);
35 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
42 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
36 } else {
43 } else {
37 commonkeywords = commonkeywords.concat(py2.keywords);
44 commonkeywords = commonkeywords.concat(py2.keywords);
38 commontypes = commontypes.concat(py2.types);
45 commonBuiltins = commonBuiltins.concat(py2.builtins);
39 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
46 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
40 }
47 }
41 var keywords = wordRegexp(commonkeywords);
48 var keywords = wordRegexp(commonkeywords);
42 var types = wordRegexp(commontypes);
49 var builtins = wordRegexp(commonBuiltins);
43
50
44 var indentInfo = null;
51 var indentInfo = null;
45
52
@@ -129,14 +136,14 CodeMirror.defineMode("python", function(conf, parserConf) {
129 return null;
136 return null;
130 }
137 }
131
138
132 if (stream.match(types)) {
133 return 'builtin';
134 }
135
136 if (stream.match(keywords)) {
139 if (stream.match(keywords)) {
137 return 'keyword';
140 return 'keyword';
138 }
141 }
139
142
143 if (stream.match(builtins)) {
144 return 'builtin';
145 }
146
140 if (stream.match(identifiers)) {
147 if (stream.match(identifiers)) {
141 return 'variable';
148 return 'variable';
142 }
149 }
@@ -244,7 +251,7 CodeMirror.defineMode("python", function(conf, parserConf) {
244 if (current === '.') {
251 if (current === '.') {
245 style = state.tokenize(stream, state);
252 style = state.tokenize(stream, state);
246 current = stream.current();
253 current = stream.current();
247 if (style === 'variable') {
254 if (style === 'variable' || style === 'builtin') {
248 return 'variable';
255 return 'variable';
249 } else {
256 } else {
250 return ERRORCLASS;
257 return ERRORCLASS;
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -47,9 +47,17 CodeMirror.defineMode("xml", function(config, parserConfig) {
47 }
47 }
48 }
48 }
49 else if (ch == "&") {
49 else if (ch == "&") {
50 stream.eatWhile(/[^;]/);
50 var ok;
51 stream.eat(";");
51 if (stream.eat("#")) {
52 return "atom";
52 if (stream.eat("x")) {
53 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
54 } else {
55 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
56 }
57 } else {
58 ok = stream.eatWhile(/[\w]/) && stream.eat(";");
59 }
60 return ok ? "atom" : "error";
53 }
61 }
54 else {
62 else {
55 stream.eatWhile(/[^&<]/);
63 stream.eatWhile(/[^&<]/);
@@ -1,5 +1,5
1 .cm-s-cobalt { background: #002240; color: white; }
1 .cm-s-cobalt { background: #002240; color: white; }
2 .cm-s-cobalt span.CodeMirror-selected { background: #b36539 !important; }
2 .cm-s-cobalt div.CodeMirror-selected { background: #b36539 !important; }
3 .cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; }
3 .cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; }
4 .cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; }
4 .cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; }
5 .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; }
5 .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; }
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -1,7 +1,7
1 /* Based on Sublime Text's Monokai theme */
1 /* Based on Sublime Text's Monokai theme */
2
2
3 .cm-s-monokai {background: #272822; color: #f8f8f2;}
3 .cm-s-monokai {background: #272822; color: #f8f8f2;}
4 .cm-s-monokai span.CodeMirror-selected {background: #ffe792 !important;}
4 .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;}
5 .cm-s-monokai .CodeMirror-gutter {background: #272822; border-right: 0px;}
5 .cm-s-monokai .CodeMirror-gutter {background: #272822; border-right: 0px;}
6 .cm-s-monokai .CodeMirror-gutter-text {color: #d0d0d0;}
6 .cm-s-monokai .CodeMirror-gutter-text {color: #d0d0d0;}
7 .cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;}
7 .cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;}
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
@@ -1,7 +1,7
1 /* Loosely based on the Midnight Textmate theme */
1 /* Loosely based on the Midnight Textmate theme */
2
2
3 .cm-s-night { background: #0a001f; color: #f8f8f8; }
3 .cm-s-night { background: #0a001f; color: #f8f8f8; }
4 .cm-s-night span.CodeMirror-selected { background: #a8f !important; }
4 .cm-s-night div.CodeMirror-selected { background: #a8f !important; }
5 .cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
5 .cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
6 .cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
6 .cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
7 .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
7 .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
@@ -1,7 +1,7
1 .cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */
1 .cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */
2
2
3 .cm-s-rubyblue { background: #112435; color: white; }
3 .cm-s-rubyblue { background: #112435; color: white; }
4 .cm-s-rubyblue span.CodeMirror-selected { background: #0000FF !important; }
4 .cm-s-rubyblue div.CodeMirror-selected { background: #0000FF !important; }
5 .cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; }
5 .cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; }
6 .cm-s-rubyblue .CodeMirror-gutter-text { color: white; }
6 .cm-s-rubyblue .CodeMirror-gutter-text { color: white; }
7 .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; }
7 .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; }
@@ -530,11 +530,11 var IPython = (function (IPython) {
530
530
531 CodeCell.prototype.select = function () {
531 CodeCell.prototype.select = function () {
532 IPython.Cell.prototype.select.apply(this);
532 IPython.Cell.prototype.select.apply(this);
533 // In some cases (inserting a new cell) we need a refresh before and
534 // after the focus. Not sure why this is the case.
535 this.code_mirror.refresh();
533 this.code_mirror.refresh();
536 this.code_mirror.focus();
534 this.code_mirror.focus();
537 this.code_mirror.refresh();
535 // We used to need an additional refresh() after the focus, but
536 // it appears that this has been fixed in CM. This bug would show
537 // up on FF when a newly loaded markdown cell was edited.
538 };
538 };
539
539
540
540
@@ -96,14 +96,11 var IPython = (function (IPython) {
96 var output = text_cell.find("div.text_cell_render");
96 var output = text_cell.find("div.text_cell_render");
97 output.hide();
97 output.hide();
98 text_cell.find('div.text_cell_input').show();
98 text_cell.find('div.text_cell_input').show();
99 // I don't know why I need to do this, but if I don't do
100 // refresh/focus/refresh, the to_markdown method won't work.
101 this.code_mirror.refresh();
99 this.code_mirror.refresh();
102 this.code_mirror.focus();
100 this.code_mirror.focus();
103 // This final refresh is needed on Firefox to trigger the editor
101 // We used to need an additional refresh() after the focus, but
104 // to be auto-sized. This glitch only happens on cell that are
102 // it appears that this has been fixed in CM. This bug would show
105 // loaded initially and haven't had their editor focused before.
103 // up on FF when a newly loaded markdown cell was edited.
106 this.code_mirror.refresh();
107 this.rendered = false;
104 this.rendered = false;
108 if (this.get_text() === this.placeholder) {
105 if (this.get_text() === this.placeholder) {
109 this.set_text('');
106 this.set_text('');
General Comments 0
You need to be logged in to leave comments. Login now