OLD | NEW |
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others |
2 // Distributed under an MIT license: http://codemirror.net/LICENSE | 2 // Distributed under an MIT license: http://codemirror.net/LICENSE |
3 | 3 |
4 // This is CodeMirror (http://codemirror.net), a code editor | 4 // This is CodeMirror (http://codemirror.net), a code editor |
5 // implemented in JavaScript on top of the browser's DOM. | 5 // implemented in JavaScript on top of the browser's DOM. |
6 // | 6 // |
7 // You can find some technical background for some of the code below | 7 // You can find some technical background for some of the code below |
8 // at http://marijnhaverbeke.nl/blog/#cm-internals . | 8 // at http://marijnhaverbeke.nl/blog/#cm-internals . |
9 | 9 |
10 (function(mod) { | 10 (function(mod) { |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 cm.curOp.forceUpdate = true; | 101 cm.curOp.forceUpdate = true; |
102 attachDoc(cm, doc); | 102 attachDoc(cm, doc); |
103 | 103 |
104 if ((options.autofocus && !mobile) || activeElt() == display.input) | 104 if ((options.autofocus && !mobile) || activeElt() == display.input) |
105 setTimeout(bind(onFocus, cm), 20); | 105 setTimeout(bind(onFocus, cm), 20); |
106 else | 106 else |
107 onBlur(cm); | 107 onBlur(cm); |
108 | 108 |
109 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) | 109 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) |
110 optionHandlers[opt](cm, options[opt], Init); | 110 optionHandlers[opt](cm, options[opt], Init); |
| 111 maybeUpdateLineNumberWidth(cm); |
111 for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm); | 112 for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm); |
112 }); | 113 }); |
113 } | 114 } |
114 | 115 |
115 // DISPLAY CONSTRUCTOR | 116 // DISPLAY CONSTRUCTOR |
116 | 117 |
117 // The display handles the DOM integration, both for input reading | 118 // The display handles the DOM integration, both for input reading |
118 // and content drawing. It holds references to DOM nodes and | 119 // and content drawing. It holds references to DOM nodes and |
119 // display-related state. | 120 // display-related state. |
120 | 121 |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
460 operation(cm, onMouseDown)(e); | 461 operation(cm, onMouseDown)(e); |
461 }; | 462 }; |
462 on(d.scrollbarV, "mousedown", barMouseDown); | 463 on(d.scrollbarV, "mousedown", barMouseDown); |
463 on(d.scrollbarH, "mousedown", barMouseDown); | 464 on(d.scrollbarH, "mousedown", barMouseDown); |
464 } | 465 } |
465 cm.state.checkedOverlayScrollbar = true; | 466 cm.state.checkedOverlayScrollbar = true; |
466 } | 467 } |
467 } | 468 } |
468 | 469 |
469 // Compute the lines that are visible in a given viewport (defaults | 470 // Compute the lines that are visible in a given viewport (defaults |
470 // the the current scroll position). viewPort may contain top, | 471 // the the current scroll position). viewport may contain top, |
471 // height, and ensure (see op.scrollToPos) properties. | 472 // height, and ensure (see op.scrollToPos) properties. |
472 function visibleLines(display, doc, viewPort) { | 473 function visibleLines(display, doc, viewport) { |
473 var top = viewPort && viewPort.top != null ? Math.max(0, viewPort.top) : dis
play.scroller.scrollTop; | 474 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : dis
play.scroller.scrollTop; |
474 top = Math.floor(top - paddingTop(display)); | 475 top = Math.floor(top - paddingTop(display)); |
475 var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + d
isplay.wrapper.clientHeight; | 476 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + d
isplay.wrapper.clientHeight; |
476 | 477 |
477 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); | 478 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); |
478 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and | 479 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and |
479 // forces those lines into the viewport (if possible). | 480 // forces those lines into the viewport (if possible). |
480 if (viewPort && viewPort.ensure) { | 481 if (viewport && viewport.ensure) { |
481 var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.
line; | 482 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.
line; |
482 if (ensureFrom < from) | 483 if (ensureFrom < from) |
483 return {from: ensureFrom, | 484 return {from: ensureFrom, |
484 to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + d
isplay.wrapper.clientHeight)}; | 485 to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + d
isplay.wrapper.clientHeight)}; |
485 if (Math.min(ensureTo, doc.lastLine()) >= to) | 486 if (Math.min(ensureTo, doc.lastLine()) >= to) |
486 return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - d
isplay.wrapper.clientHeight), | 487 return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - d
isplay.wrapper.clientHeight), |
487 to: ensureTo}; | 488 to: ensureTo}; |
488 } | 489 } |
489 return {from: from, to: Math.max(to, from + 1)}; | 490 return {from: from, to: Math.max(to, from + 1)}; |
490 } | 491 } |
491 | 492 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 | 537 |
537 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, | 538 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, |
538 // but using getBoundingClientRect to get a sub-pixel-accurate | 539 // but using getBoundingClientRect to get a sub-pixel-accurate |
539 // result. | 540 // result. |
540 function compensateForHScroll(display) { | 541 function compensateForHScroll(display) { |
541 return display.scroller.getBoundingClientRect().left - display.sizer.getBoun
dingClientRect().left; | 542 return display.scroller.getBoundingClientRect().left - display.sizer.getBoun
dingClientRect().left; |
542 } | 543 } |
543 | 544 |
544 // DISPLAY DRAWING | 545 // DISPLAY DRAWING |
545 | 546 |
546 // Updates the display, selection, and scrollbars, using the | 547 function DisplayUpdate(cm, viewport, force) { |
547 // information in display.view to find out which nodes are no longer | 548 var display = cm.display; |
548 // up-to-date. Tries to bail out early when no changes are needed, | |
549 // unless forced is true. | |
550 // Returns true if an actual update happened, false otherwise. | |
551 function updateDisplay(cm, viewPort, forced) { | |
552 var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated; | |
553 var visible = visibleLines(cm.display, cm.doc, viewPort); | |
554 for (var first = true;; first = false) { | |
555 var oldWidth = cm.display.scroller.clientWidth; | |
556 if (!updateDisplayInner(cm, visible, forced)) break; | |
557 updated = true; | |
558 | 549 |
559 // If the max line changed since it was last measured, measure it, | 550 this.viewport = viewport; |
560 // and ensure the document's width matches it. | 551 // Store some values that we'll need later (but don't want to force a relayo
ut for) |
561 if (cm.display.maxLineChanged && !cm.options.lineWrapping) | 552 this.visible = visibleLines(display, cm.doc, viewport); |
562 adjustContentWidth(cm); | 553 this.editorIsHidden = !display.wrapper.offsetWidth; |
563 | 554 this.wrapperHeight = display.wrapper.clientHeight; |
564 var barMeasure = measureForScrollbars(cm); | 555 this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo; |
565 updateSelection(cm); | 556 this.oldScrollerWidth = display.scroller.clientWidth; |
566 setDocumentHeight(cm, barMeasure); | 557 this.force = force; |
567 updateScrollbars(cm, barMeasure); | 558 this.dims = getDimensions(cm); |
568 if (webkit && cm.options.lineWrapping) | |
569 checkForWebkitWidthBug(cm, barMeasure); // (Issue #2420) | |
570 if (webkit && barMeasure.scrollWidth > barMeasure.clientWidth && | |
571 barMeasure.scrollWidth < barMeasure.clientWidth + 1 && | |
572 !hScrollbarTakesSpace(cm)) | |
573 updateScrollbars(cm); // (Issue #2562) | |
574 if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.cl
ientWidth) { | |
575 forced = true; | |
576 continue; | |
577 } | |
578 forced = false; | |
579 | |
580 // Clip forced viewport to actual scrollable area. | |
581 if (viewPort && viewPort.top != null) | |
582 viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMea
sure.clientHeight, viewPort.top)}; | |
583 // Updated line heights might result in the drawn area not | |
584 // actually covering the viewport. Keep looping until it does. | |
585 visible = visibleLines(cm.display, cm.doc, viewPort); | |
586 if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo
) | |
587 break; | |
588 } | |
589 | |
590 cm.display.updateLineNumbers = null; | |
591 if (updated) { | |
592 signalLater(cm, "update", cm); | |
593 if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo) | |
594 signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.vi
ewTo); | |
595 } | |
596 return updated; | |
597 } | 559 } |
598 | 560 |
599 // Does the actual updating of the line display. Bails out | 561 // Does the actual updating of the line display. Bails out |
600 // (returning false) when there is nothing to be done and forced is | 562 // (returning false) when there is nothing to be done and forced is |
601 // false. | 563 // false. |
602 function updateDisplayInner(cm, visible, forced) { | 564 function updateDisplayIfNeeded(cm, update) { |
603 var display = cm.display, doc = cm.doc; | 565 var display = cm.display, doc = cm.doc; |
604 if (!display.wrapper.offsetWidth) { | 566 if (update.editorIsHidden) { |
605 resetView(cm); | 567 resetView(cm); |
606 return; | 568 return false; |
607 } | 569 } |
608 | 570 |
609 // Bail out if the visible area is already rendered and nothing changed. | 571 // Bail out if the visible area is already rendered and nothing changed. |
610 if (!forced && visible.from >= display.viewFrom && visible.to <= display.vie
wTo && | 572 if (!update.force && |
| 573 update.visible.from >= display.viewFrom && update.visible.to <= display.
viewTo && |
611 (display.updateLineNumbers == null || display.updateLineNumbers >= displ
ay.viewTo) && | 574 (display.updateLineNumbers == null || display.updateLineNumbers >= displ
ay.viewTo) && |
612 countDirtyView(cm) == 0) | 575 countDirtyView(cm) == 0) |
613 return; | 576 return false; |
614 | 577 |
615 if (maybeUpdateLineNumberWidth(cm)) | 578 if (maybeUpdateLineNumberWidth(cm)) { |
616 resetView(cm); | 579 resetView(cm); |
617 var dims = getDimensions(cm); | 580 update.dims = getDimensions(cm); |
| 581 } |
618 | 582 |
619 // Compute a suitable new viewport (from & to) | 583 // Compute a suitable new viewport (from & to) |
620 var end = doc.first + doc.size; | 584 var end = doc.first + doc.size; |
621 var from = Math.max(visible.from - cm.options.viewportMargin, doc.first); | 585 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.fir
st); |
622 var to = Math.min(end, visible.to + cm.options.viewportMargin); | 586 var to = Math.min(end, update.visible.to + cm.options.viewportMargin); |
623 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max
(doc.first, display.viewFrom); | 587 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max
(doc.first, display.viewFrom); |
624 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, disp
lay.viewTo); | 588 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, disp
lay.viewTo); |
625 if (sawCollapsedSpans) { | 589 if (sawCollapsedSpans) { |
626 from = visualLineNo(cm.doc, from); | 590 from = visualLineNo(cm.doc, from); |
627 to = visualLineEndNo(cm.doc, to); | 591 to = visualLineEndNo(cm.doc, to); |
628 } | 592 } |
629 | 593 |
630 var different = from != display.viewFrom || to != display.viewTo || | 594 var different = from != display.viewFrom || to != display.viewTo || |
631 display.lastSizeC != display.wrapper.clientHeight; | 595 display.lastSizeC != update.wrapperHeight; |
632 adjustView(cm, from, to); | 596 adjustView(cm, from, to); |
633 | 597 |
634 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); | 598 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); |
635 // Position the mover div to align with the current scroll position | 599 // Position the mover div to align with the current scroll position |
636 cm.display.mover.style.top = display.viewOffset + "px"; | 600 cm.display.mover.style.top = display.viewOffset + "px"; |
637 | 601 |
638 var toUpdate = countDirtyView(cm); | 602 var toUpdate = countDirtyView(cm); |
639 if (!different && toUpdate == 0 && !forced) return; | 603 if (!different && toUpdate == 0 && !update.force && |
| 604 (display.updateLineNumbers == null || display.updateLineNumbers >= displ
ay.viewTo)) |
| 605 return false; |
640 | 606 |
641 // For big changes, we hide the enclosing element during the | 607 // For big changes, we hide the enclosing element during the |
642 // update, since that speeds up the operations on most browsers. | 608 // update, since that speeds up the operations on most browsers. |
643 var focused = activeElt(); | 609 var focused = activeElt(); |
644 if (toUpdate > 4) display.lineDiv.style.display = "none"; | 610 if (toUpdate > 4) display.lineDiv.style.display = "none"; |
645 patchDisplay(cm, display.updateLineNumbers, dims); | 611 patchDisplay(cm, display.updateLineNumbers, update.dims); |
646 if (toUpdate > 4) display.lineDiv.style.display = ""; | 612 if (toUpdate > 4) display.lineDiv.style.display = ""; |
647 // There might have been a widget with a focused element that got | 613 // There might have been a widget with a focused element that got |
648 // hidden or updated, if so re-focus it. | 614 // hidden or updated, if so re-focus it. |
649 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus
(); | 615 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus
(); |
650 | 616 |
651 // Prevent selection and cursors from interfering with the scroll | 617 // Prevent selection and cursors from interfering with the scroll |
652 // width. | 618 // width. |
653 removeChildren(display.cursorDiv); | 619 removeChildren(display.cursorDiv); |
654 removeChildren(display.selectionDiv); | 620 removeChildren(display.selectionDiv); |
655 | 621 |
656 if (different) { | 622 if (different) { |
657 display.lastSizeC = display.wrapper.clientHeight; | 623 display.lastSizeC = update.wrapperHeight; |
658 startWorker(cm, 400); | 624 startWorker(cm, 400); |
659 } | 625 } |
660 | 626 |
661 updateHeightsInViewport(cm); | 627 display.updateLineNumbers = null; |
662 | 628 |
663 return true; | 629 return true; |
664 } | 630 } |
665 | 631 |
666 function adjustContentWidth(cm) { | 632 function postUpdateDisplay(cm, update) { |
667 var display = cm.display; | 633 var force = update.force, viewport = update.viewport; |
668 var width = measureChar(cm, display.maxLine, display.maxLine.text.length).le
ft; | 634 for (var first = true;; first = false) { |
669 display.maxLineChanged = false; | 635 if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.disp
lay.scroller.clientWidth) { |
670 var minWidth = Math.max(0, width + 3); | 636 force = true; |
671 var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scroll
erCutOff - display.scroller.clientWidth); | 637 } else { |
672 display.sizer.style.minWidth = minWidth + "px"; | 638 force = false; |
673 if (maxScrollLeft < cm.doc.scrollLeft) | 639 // Clip forced viewport to actual scrollable area. |
674 setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), tr
ue); | 640 if (viewport && viewport.top != null) |
| 641 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - sc
rollerCutOff - |
| 642 cm.display.scroller.clientHeight, viewport.t
op)}; |
| 643 // Updated line heights might result in the drawn area not |
| 644 // actually covering the viewport. Keep looping until it does. |
| 645 update.visible = visibleLines(cm.display, cm.doc, viewport); |
| 646 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= c
m.display.viewTo) |
| 647 break; |
| 648 } |
| 649 if (!updateDisplayIfNeeded(cm, update)) break; |
| 650 updateHeightsInViewport(cm); |
| 651 var barMeasure = measureForScrollbars(cm); |
| 652 updateSelection(cm); |
| 653 setDocumentHeight(cm, barMeasure); |
| 654 updateScrollbars(cm, barMeasure); |
| 655 } |
| 656 |
| 657 signalLater(cm, "update", cm); |
| 658 if (cm.display.viewFrom != update.oldViewFrom || cm.display.viewTo != update
.oldViewTo) |
| 659 signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.view
To); |
| 660 } |
| 661 |
| 662 function updateDisplaySimple(cm, viewport) { |
| 663 var update = new DisplayUpdate(cm, viewport); |
| 664 if (updateDisplayIfNeeded(cm, update)) { |
| 665 updateHeightsInViewport(cm); |
| 666 postUpdateDisplay(cm, update); |
| 667 var barMeasure = measureForScrollbars(cm); |
| 668 updateSelection(cm); |
| 669 setDocumentHeight(cm, barMeasure); |
| 670 updateScrollbars(cm, barMeasure); |
| 671 } |
675 } | 672 } |
676 | 673 |
677 function setDocumentHeight(cm, measure) { | 674 function setDocumentHeight(cm, measure) { |
678 cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measu
re.docHeight + "px"; | 675 cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measu
re.docHeight + "px"; |
679 cm.display.gutters.style.height = Math.max(measure.docHeight, measure.client
Height - scrollerCutOff) + "px"; | 676 cm.display.gutters.style.height = Math.max(measure.docHeight, measure.client
Height - scrollerCutOff) + "px"; |
680 } | 677 } |
681 | 678 |
682 function checkForWebkitWidthBug(cm, measure) { | 679 function checkForWebkitWidthBug(cm, measure) { |
683 // Work around Webkit bug where it sometimes reserves space for a | 680 // Work around Webkit bug where it sometimes reserves space for a |
684 // non-existing phantom scrollbar in the scroller (Issue #2420) | 681 // non-existing phantom scrollbar in the scroller (Issue #2420) |
(...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1250 } | 1247 } |
1251 } | 1248 } |
1252 } | 1249 } |
1253 return curPos; | 1250 return curPos; |
1254 } | 1251 } |
1255 } | 1252 } |
1256 | 1253 |
1257 // SELECTION DRAWING | 1254 // SELECTION DRAWING |
1258 | 1255 |
1259 // Redraw the selection and/or cursor | 1256 // Redraw the selection and/or cursor |
1260 function updateSelection(cm) { | 1257 function drawSelection(cm) { |
1261 var display = cm.display, doc = cm.doc; | 1258 var display = cm.display, doc = cm.doc, result = {}; |
1262 var curFragment = document.createDocumentFragment(); | 1259 var curFragment = result.cursors = document.createDocumentFragment(); |
1263 var selFragment = document.createDocumentFragment(); | 1260 var selFragment = result.selection = document.createDocumentFragment(); |
1264 | 1261 |
1265 for (var i = 0; i < doc.sel.ranges.length; i++) { | 1262 for (var i = 0; i < doc.sel.ranges.length; i++) { |
1266 var range = doc.sel.ranges[i]; | 1263 var range = doc.sel.ranges[i]; |
1267 var collapsed = range.empty(); | 1264 var collapsed = range.empty(); |
1268 if (collapsed || cm.options.showCursorWhenSelecting) | 1265 if (collapsed || cm.options.showCursorWhenSelecting) |
1269 drawSelectionCursor(cm, range, curFragment); | 1266 drawSelectionCursor(cm, range, curFragment); |
1270 if (!collapsed) | 1267 if (!collapsed) |
1271 drawSelectionRange(cm, range, selFragment); | 1268 drawSelectionRange(cm, range, selFragment); |
1272 } | 1269 } |
1273 | 1270 |
1274 // Move the hidden textarea near the cursor to prevent scrolling artifacts | 1271 // Move the hidden textarea near the cursor to prevent scrolling artifacts |
1275 if (cm.options.moveInputWithCursor) { | 1272 if (cm.options.moveInputWithCursor) { |
1276 var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); | 1273 var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); |
1277 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.l
ineDiv.getBoundingClientRect(); | 1274 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.l
ineDiv.getBoundingClientRect(); |
1278 var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, | 1275 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, |
1279 headPos.top + lineOff.top - wrapOff.top)); | 1276 headPos.top + lineOff.top - wrapOff.to
p)); |
1280 var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, | 1277 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, |
1281 headPos.left + lineOff.left - wrapOff.left
)); | 1278 headPos.left + lineOff.left - wrapOff
.left)); |
1282 display.inputDiv.style.top = top + "px"; | |
1283 display.inputDiv.style.left = left + "px"; | |
1284 } | 1279 } |
1285 | 1280 |
1286 removeChildrenAndAdd(display.cursorDiv, curFragment); | 1281 return result; |
1287 removeChildrenAndAdd(display.selectionDiv, selFragment); | 1282 } |
| 1283 |
| 1284 function showSelection(cm, drawn) { |
| 1285 removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors); |
| 1286 removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection); |
| 1287 if (drawn.teTop != null) { |
| 1288 cm.display.inputDiv.style.top = drawn.teTop + "px"; |
| 1289 cm.display.inputDiv.style.left = drawn.teLeft + "px"; |
| 1290 } |
| 1291 } |
| 1292 |
| 1293 function updateSelection(cm) { |
| 1294 showSelection(cm, drawSelection(cm)); |
1288 } | 1295 } |
1289 | 1296 |
1290 // Draws a cursor for the given range | 1297 // Draws a cursor for the given range |
1291 function drawSelectionCursor(cm, range, output) { | 1298 function drawSelectionCursor(cm, range, output) { |
1292 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.single
CursorHeightPerLine); | 1299 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.single
CursorHeightPerLine); |
1293 | 1300 |
1294 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); | 1301 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); |
1295 cursor.style.left = pos.left + "px"; | 1302 cursor.style.left = pos.left + "px"; |
1296 cursor.style.top = pos.top + "px"; | 1303 cursor.style.top = pos.top + "px"; |
1297 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH
eight + "px"; | 1304 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH
eight + "px"; |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1401 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) | 1408 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) |
1402 cm.state.highlight.set(time, bind(highlightWorker, cm)); | 1409 cm.state.highlight.set(time, bind(highlightWorker, cm)); |
1403 } | 1410 } |
1404 | 1411 |
1405 function highlightWorker(cm) { | 1412 function highlightWorker(cm) { |
1406 var doc = cm.doc; | 1413 var doc = cm.doc; |
1407 if (doc.frontier < doc.first) doc.frontier = doc.first; | 1414 if (doc.frontier < doc.first) doc.frontier = doc.first; |
1408 if (doc.frontier >= cm.display.viewTo) return; | 1415 if (doc.frontier >= cm.display.viewTo) return; |
1409 var end = +new Date + cm.options.workTime; | 1416 var end = +new Date + cm.options.workTime; |
1410 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); | 1417 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); |
| 1418 var changedLines = []; |
1411 | 1419 |
1412 runInOp(cm, function() { | |
1413 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50
0), function(line) { | 1420 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50
0), function(line) { |
1414 if (doc.frontier >= cm.display.viewFrom) { // Visible | 1421 if (doc.frontier >= cm.display.viewFrom) { // Visible |
1415 var oldStyles = line.styles; | 1422 var oldStyles = line.styles; |
1416 var highlighted = highlightLine(cm, line, state, true); | 1423 var highlighted = highlightLine(cm, line, state, true); |
1417 line.styles = highlighted.styles; | 1424 line.styles = highlighted.styles; |
1418 var oldCls = line.styleClasses, newCls = highlighted.classes; | 1425 var oldCls = line.styleClasses, newCls = highlighted.classes; |
1419 if (newCls) line.styleClasses = newCls; | 1426 if (newCls) line.styleClasses = newCls; |
1420 else if (oldCls) line.styleClasses = null; | 1427 else if (oldCls) line.styleClasses = null; |
1421 var ischange = !oldStyles || oldStyles.length != line.styles.length || | 1428 var ischange = !oldStyles || oldStyles.length != line.styles.length || |
1422 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg
Class || oldCls.textClass != newCls.textClass); | 1429 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg
Class || oldCls.textClass != newCls.textClass); |
1423 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt
yles[i] != line.styles[i]; | 1430 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt
yles[i] != line.styles[i]; |
1424 if (ischange) regLineChange(cm, doc.frontier, "text"); | 1431 if (ischange) changedLines.push(doc.frontier); |
1425 line.stateAfter = copyState(doc.mode, state); | 1432 line.stateAfter = copyState(doc.mode, state); |
1426 } else { | 1433 } else { |
1427 processLine(cm, line.text, state); | 1434 processLine(cm, line.text, state); |
1428 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n
ull; | 1435 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n
ull; |
1429 } | 1436 } |
1430 ++doc.frontier; | 1437 ++doc.frontier; |
1431 if (+new Date > end) { | 1438 if (+new Date > end) { |
1432 startWorker(cm, cm.options.workDelay); | 1439 startWorker(cm, cm.options.workDelay); |
1433 return true; | 1440 return true; |
1434 } | 1441 } |
1435 }); | 1442 }); |
| 1443 if (changedLines.length) runInOp(cm, function() { |
| 1444 for (var i = 0; i < changedLines.length; i++) |
| 1445 regLineChange(cm, changedLines[i], "text"); |
1436 }); | 1446 }); |
1437 } | 1447 } |
1438 | 1448 |
1439 // Finds the line to start with when starting a parse. Tries to | 1449 // Finds the line to start with when starting a parse. Tries to |
1440 // find a line with a stateAfter, so that it can start with a | 1450 // find a line with a stateAfter, so that it can start with a |
1441 // valid state. If that fails, it returns the line with the | 1451 // valid state. If that fails, it returns the line with the |
1442 // smallest indentation, which tends to need the least context to | 1452 // smallest indentation, which tends to need the least context to |
1443 // parse correctly. | 1453 // parse correctly. |
1444 function findStartLine(cm, n, precise) { | 1454 function findStartLine(cm, n, precise) { |
1445 var minindent, minline, doc = cm.doc; | 1455 var minindent, minline, doc = cm.doc; |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1658 rect = node.getBoundingClientRect(); | 1668 rect = node.getBoundingClientRect(); |
1659 } | 1669 } |
1660 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right))
{ | 1670 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right))
{ |
1661 var rSpan = node.parentNode.getClientRects()[0]; | 1671 var rSpan = node.parentNode.getClientRects()[0]; |
1662 if (rSpan) | 1672 if (rSpan) |
1663 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top
: rSpan.top, bottom: rSpan.bottom}; | 1673 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top
: rSpan.top, bottom: rSpan.bottom}; |
1664 else | 1674 else |
1665 rect = nullRect; | 1675 rect = nullRect; |
1666 } | 1676 } |
1667 | 1677 |
| 1678 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measu
re, rect); |
| 1679 |
1668 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.
top; | 1680 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.
top; |
1669 var mid = (rtop + rbot) / 2; | 1681 var mid = (rtop + rbot) / 2; |
1670 var heights = prepared.view.measure.heights; | 1682 var heights = prepared.view.measure.heights; |
1671 for (var i = 0; i < heights.length - 1; i++) | 1683 for (var i = 0; i < heights.length - 1; i++) |
1672 if (mid < heights[i]) break; | 1684 if (mid < heights[i]) break; |
1673 var top = i ? heights[i - 1] : 0, bot = heights[i]; | 1685 var top = i ? heights[i - 1] : 0, bot = heights[i]; |
1674 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepare
d.rect.left, | 1686 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepare
d.rect.left, |
1675 right: (collapse == "left" ? rect.left : rect.right) - prepare
d.rect.left, | 1687 right: (collapse == "left" ? rect.left : rect.right) - prepare
d.rect.left, |
1676 top: top, bottom: bot}; | 1688 top: top, bottom: bot}; |
1677 if (!rect.left && !rect.right) result.bogus = true; | 1689 if (!rect.left && !rect.right) result.bogus = true; |
1678 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbot
tom = rbot; } | 1690 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbot
tom = rbot; } |
| 1691 |
1679 return result; | 1692 return result; |
1680 } | 1693 } |
1681 | 1694 |
| 1695 // Work around problem with bounding client rects on ranges being |
| 1696 // returned incorrectly when zoomed on IE10 and below. |
| 1697 function maybeUpdateRectForZooming(measure, rect) { |
| 1698 if (!window.screen || screen.logicalXDPI == null || |
| 1699 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) |
| 1700 return rect; |
| 1701 var scaleX = screen.logicalXDPI / screen.deviceXDPI; |
| 1702 var scaleY = screen.logicalYDPI / screen.deviceYDPI; |
| 1703 return {left: rect.left * scaleX, right: rect.right * scaleX, |
| 1704 top: rect.top * scaleY, bottom: rect.bottom * scaleY}; |
| 1705 } |
| 1706 |
1682 function clearLineMeasurementCacheFor(lineView) { | 1707 function clearLineMeasurementCacheFor(lineView) { |
1683 if (lineView.measure) { | 1708 if (lineView.measure) { |
1684 lineView.measure.cache = {}; | 1709 lineView.measure.cache = {}; |
1685 lineView.measure.heights = null; | 1710 lineView.measure.heights = null; |
1686 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) | 1711 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) |
1687 lineView.measure.caches[i] = {}; | 1712 lineView.measure.caches[i] = {}; |
1688 } | 1713 } |
1689 } | 1714 } |
1690 | 1715 |
1691 function clearLineMeasurementCache(cm) { | 1716 function clearLineMeasurementCache(cm) { |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1904 } | 1929 } |
1905 | 1930 |
1906 // OPERATIONS | 1931 // OPERATIONS |
1907 | 1932 |
1908 // Operations are used to wrap a series of changes to the editor | 1933 // Operations are used to wrap a series of changes to the editor |
1909 // state in such a way that each change won't have to update the | 1934 // state in such a way that each change won't have to update the |
1910 // cursor and display (which would be awkward, slow, and | 1935 // cursor and display (which would be awkward, slow, and |
1911 // error-prone). Instead, display updates are batched and then all | 1936 // error-prone). Instead, display updates are batched and then all |
1912 // combined and executed at once. | 1937 // combined and executed at once. |
1913 | 1938 |
| 1939 var operationGroup = null; |
| 1940 |
1914 var nextOpId = 0; | 1941 var nextOpId = 0; |
1915 // Start a new operation. | 1942 // Start a new operation. |
1916 function startOperation(cm) { | 1943 function startOperation(cm) { |
1917 cm.curOp = { | 1944 cm.curOp = { |
| 1945 cm: cm, |
1918 viewChanged: false, // Flag that indicates that lines might need to b
e redrawn | 1946 viewChanged: false, // Flag that indicates that lines might need to b
e redrawn |
1919 startHeight: cm.doc.height, // Used to detect need to update scrollbar | 1947 startHeight: cm.doc.height, // Used to detect need to update scrollbar |
1920 forceUpdate: false, // Used to force a redraw | 1948 forceUpdate: false, // Used to force a redraw |
1921 updateInput: null, // Whether to reset the input textarea | 1949 updateInput: null, // Whether to reset the input textarea |
1922 typing: false, // Whether this reset should be careful to leave
existing text (for compositing) | 1950 typing: false, // Whether this reset should be careful to leave
existing text (for compositing) |
1923 changeObjs: null, // Accumulated changes, for firing change events | 1951 changeObjs: null, // Accumulated changes, for firing change events |
1924 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on | 1952 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on |
| 1953 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been
called already |
1925 selectionChanged: false, // Whether the selection needs to be redrawn | 1954 selectionChanged: false, // Whether the selection needs to be redrawn |
1926 updateMaxLine: false, // Set when the widest line needs to be determine
d anew | 1955 updateMaxLine: false, // Set when the widest line needs to be determine
d anew |
1927 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu
shed to DOM yet | 1956 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu
shed to DOM yet |
1928 scrollToPos: null, // Used to scroll to a specific position | 1957 scrollToPos: null, // Used to scroll to a specific position |
1929 id: ++nextOpId // Unique ID | 1958 id: ++nextOpId // Unique ID |
1930 }; | 1959 }; |
1931 if (!delayedCallbackDepth++) delayedCallbacks = []; | 1960 if (operationGroup) { |
| 1961 operationGroup.ops.push(cm.curOp); |
| 1962 } else { |
| 1963 cm.curOp.ownsGroup = operationGroup = { |
| 1964 ops: [cm.curOp], |
| 1965 delayedCallbacks: [] |
| 1966 }; |
| 1967 } |
| 1968 } |
| 1969 |
| 1970 function fireCallbacksForOps(group) { |
| 1971 // Calls delayed callbacks and cursorActivity handlers until no |
| 1972 // new ones appear |
| 1973 var callbacks = group.delayedCallbacks, i = 0; |
| 1974 do { |
| 1975 for (; i < callbacks.length; i++) |
| 1976 callbacks[i](); |
| 1977 for (var j = 0; j < group.ops.length; j++) { |
| 1978 var op = group.ops[j]; |
| 1979 if (op.cursorActivityHandlers) |
| 1980 while (op.cursorActivityCalled < op.cursorActivityHandlers.length) |
| 1981 op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm); |
| 1982 } |
| 1983 } while (i < callbacks.length); |
1932 } | 1984 } |
1933 | 1985 |
1934 // Finish an operation, updating the display and signalling delayed events | 1986 // Finish an operation, updating the display and signalling delayed events |
1935 function endOperation(cm) { | 1987 function endOperation(cm) { |
1936 var op = cm.curOp, doc = cm.doc, display = cm.display; | 1988 var op = cm.curOp, group = op.ownsGroup; |
1937 cm.curOp = null; | 1989 if (!group) return; |
1938 | 1990 |
| 1991 try { fireCallbacksForOps(group); } |
| 1992 finally { |
| 1993 operationGroup = null; |
| 1994 for (var i = 0; i < group.ops.length; i++) |
| 1995 group.ops[i].cm.curOp = null; |
| 1996 endOperations(group); |
| 1997 } |
| 1998 } |
| 1999 |
| 2000 // The DOM updates done when an operation finishes are batched so |
| 2001 // that the minimum number of relayouts are required. |
| 2002 function endOperations(group) { |
| 2003 var ops = group.ops; |
| 2004 for (var i = 0; i < ops.length; i++) // Read DOM |
| 2005 endOperation_R1(ops[i]); |
| 2006 for (var i = 0; i < ops.length; i++) // Write DOM (maybe) |
| 2007 endOperation_W1(ops[i]); |
| 2008 for (var i = 0; i < ops.length; i++) // Read DOM |
| 2009 endOperation_R2(ops[i]); |
| 2010 for (var i = 0; i < ops.length; i++) // Write DOM (maybe) |
| 2011 endOperation_W2(ops[i]); |
| 2012 for (var i = 0; i < ops.length; i++) // Read DOM |
| 2013 endOperation_finish(ops[i]); |
| 2014 } |
| 2015 |
| 2016 function endOperation_R1(op) { |
| 2017 var cm = op.cm, display = cm.display; |
1939 if (op.updateMaxLine) findMaxLine(cm); | 2018 if (op.updateMaxLine) findMaxLine(cm); |
1940 | 2019 |
1941 // If it looks like an update might be needed, call updateDisplay | 2020 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || |
1942 if (op.viewChanged || op.forceUpdate || op.scrollTop != null || | 2021 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || |
1943 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || | 2022 op.scrollToPos.to.line >= display.viewTo) || |
1944 op.scrollToPos.to.line >= display.viewTo) || | 2023 display.maxLineChanged && cm.options.lineWrapping; |
1945 display.maxLineChanged && cm.options.lineWrapping) { | 2024 op.update = op.mustUpdate && |
1946 var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos
}, op.forceUpdate); | 2025 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scro
llToPos}, op.forceUpdate); |
1947 if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroll
er.scrollTop; | 2026 } |
| 2027 |
| 2028 function endOperation_W1(op) { |
| 2029 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
; |
| 2030 } |
| 2031 |
| 2032 function endOperation_R2(op) { |
| 2033 var cm = op.cm, display = cm.display; |
| 2034 if (op.updatedDisplay) updateHeightsInViewport(cm); |
| 2035 |
| 2036 op.barMeasure = measureForScrollbars(cm); |
| 2037 |
| 2038 // If the max line changed since it was last measured, measure it, |
| 2039 // and ensure the document's width matches it. |
| 2040 // updateDisplay_W2 will use these properties to do the actual resizing |
| 2041 if (display.maxLineChanged && !cm.options.lineWrapping) { |
| 2042 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.l
ength).left + 3; |
| 2043 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo
+ |
| 2044 scrollerCutOff - display.scroller.clientWidth)
; |
1948 } | 2045 } |
1949 // If no update was run, but the selection changed, redraw that. | 2046 |
1950 if (!updated && op.selectionChanged) updateSelection(cm); | 2047 if (op.updatedDisplay || op.selectionChanged) |
1951 if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm); | 2048 op.newSelectionNodes = drawSelection(cm); |
| 2049 } |
| 2050 |
| 2051 function endOperation_W2(op) { |
| 2052 var cm = op.cm; |
| 2053 |
| 2054 if (op.adjustWidthTo != null) { |
| 2055 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; |
| 2056 if (op.maxScrollLeft < cm.doc.scrollLeft) |
| 2057 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollL
eft), true); |
| 2058 cm.display.maxLineChanged = false; |
| 2059 } |
| 2060 |
| 2061 if (op.newSelectionNodes) |
| 2062 showSelection(cm, op.newSelectionNodes); |
| 2063 if (op.updatedDisplay) |
| 2064 setDocumentHeight(cm, op.barMeasure); |
| 2065 if (op.updatedDisplay || op.startHeight != cm.doc.height) |
| 2066 updateScrollbars(cm, op.barMeasure); |
| 2067 |
| 2068 if (op.selectionChanged) restartBlink(cm); |
| 2069 |
| 2070 if (cm.state.focused && op.updateInput) |
| 2071 resetInput(cm, op.typing); |
| 2072 } |
| 2073 |
| 2074 function endOperation_finish(op) { |
| 2075 var cm = op.cm, display = cm.display, doc = cm.doc; |
| 2076 |
| 2077 if (op.adjustWidthTo != null && Math.abs(op.barMeasure.scrollWidth - cm.disp
lay.scroller.scrollWidth) > 1) |
| 2078 updateScrollbars(cm); |
| 2079 |
| 2080 if (op.updatedDisplay) postUpdateDisplay(cm, op.update); |
1952 | 2081 |
1953 // Abort mouse wheel delta measurement, when scrolling explicitly | 2082 // Abort mouse wheel delta measurement, when scrolling explicitly |
1954 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft !=
null || op.scrollToPos)) | 2083 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft !=
null || op.scrollToPos)) |
1955 display.wheelStartX = display.wheelStartY = null; | 2084 display.wheelStartX = display.wheelStartY = null; |
1956 | 2085 |
1957 // Propagate the scroll position to the actual DOM scroller | 2086 // Propagate the scroll position to the actual DOM scroller |
1958 if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) { | 2087 if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) { |
1959 var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scr
oller.clientHeight, op.scrollTop)); | 2088 var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scr
oller.clientHeight, op.scrollTop)); |
1960 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop
= top; | 2089 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop
= top; |
1961 } | 2090 } |
1962 if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) { | 2091 if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) { |
1963 var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scr
oller.clientWidth, op.scrollLeft)); | 2092 var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scr
oller.clientWidth, op.scrollLeft)); |
1964 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLe
ft = left; | 2093 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLe
ft = left; |
1965 alignHorizontally(cm); | 2094 alignHorizontally(cm); |
1966 } | 2095 } |
1967 // If we need to scroll a specific position into view, do so. | 2096 // If we need to scroll a specific position into view, do so. |
1968 if (op.scrollToPos) { | 2097 if (op.scrollToPos) { |
1969 var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from), | 2098 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), |
1970 clipPos(cm.doc, op.scrollToPos.to), op.scro
llToPos.margin); | 2099 clipPos(doc, op.scrollToPos.to), op.scrollT
oPos.margin); |
1971 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo
rds); | 2100 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo
rds); |
1972 } | 2101 } |
1973 | 2102 |
1974 if (op.selectionChanged) restartBlink(cm); | |
1975 | |
1976 if (cm.state.focused && op.updateInput) | |
1977 resetInput(cm, op.typing); | |
1978 | |
1979 // Fire events for markers that are hidden/unidden by editing or | 2103 // Fire events for markers that are hidden/unidden by editing or |
1980 // undoing | 2104 // undoing |
1981 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; | 2105 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; |
1982 if (hidden) for (var i = 0; i < hidden.length; ++i) | 2106 if (hidden) for (var i = 0; i < hidden.length; ++i) |
1983 if (!hidden[i].lines.length) signal(hidden[i], "hide"); | 2107 if (!hidden[i].lines.length) signal(hidden[i], "hide"); |
1984 if (unhidden) for (var i = 0; i < unhidden.length; ++i) | 2108 if (unhidden) for (var i = 0; i < unhidden.length; ++i) |
1985 if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); | 2109 if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); |
1986 | 2110 |
1987 var delayed; | 2111 if (display.wrapper.offsetHeight) |
1988 if (!--delayedCallbackDepth) { | 2112 doc.scrollTop = cm.display.scroller.scrollTop; |
1989 delayed = delayedCallbacks; | 2113 |
1990 delayedCallbacks = null; | 2114 // Apply workaround for two webkit bugs |
| 2115 if (op.updatedDisplay && webkit) { |
| 2116 if (cm.options.lineWrapping) |
| 2117 checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420) |
| 2118 if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth && |
| 2119 op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 && |
| 2120 !hScrollbarTakesSpace(cm)) |
| 2121 updateScrollbars(cm); // (Issue #2562) |
1991 } | 2122 } |
| 2123 |
1992 // Fire change events, and delayed event handlers | 2124 // Fire change events, and delayed event handlers |
1993 if (op.changeObjs) | 2125 if (op.changeObjs) |
1994 signal(cm, "changes", cm, op.changeObjs); | 2126 signal(cm, "changes", cm, op.changeObjs); |
1995 if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i](); | |
1996 if (op.cursorActivityHandlers) | |
1997 for (var i = 0; i < op.cursorActivityHandlers.length; i++) | |
1998 op.cursorActivityHandlers[i](cm); | |
1999 } | 2127 } |
2000 | 2128 |
2001 // Run the given function in an operation | 2129 // Run the given function in an operation |
2002 function runInOp(cm, f) { | 2130 function runInOp(cm, f) { |
2003 if (cm.curOp) return f(); | 2131 if (cm.curOp) return f(); |
2004 startOperation(cm); | 2132 startOperation(cm); |
2005 try { return f(); } | 2133 try { return f(); } |
2006 finally { endOperation(cm); } | 2134 finally { endOperation(cm); } |
2007 } | 2135 } |
2008 // Wraps a function in an operation. Returns the wrapped function. | 2136 // Wraps a function in an operation. Returns the wrapped function. |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2240 var missed = false; | 2368 var missed = false; |
2241 cm.display.pollingFast = true; | 2369 cm.display.pollingFast = true; |
2242 function p() { | 2370 function p() { |
2243 var changed = readInput(cm); | 2371 var changed = readInput(cm); |
2244 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);} | 2372 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);} |
2245 else {cm.display.pollingFast = false; slowPoll(cm);} | 2373 else {cm.display.pollingFast = false; slowPoll(cm);} |
2246 } | 2374 } |
2247 cm.display.poll.set(20, p); | 2375 cm.display.poll.set(20, p); |
2248 } | 2376 } |
2249 | 2377 |
| 2378 // This will be set to an array of strings when copying, so that, |
| 2379 // when pasting, we know what kind of selections the copied text |
| 2380 // was made out of. |
| 2381 var lastCopied = null; |
| 2382 |
2250 // Read input from the textarea, and update the document to match. | 2383 // Read input from the textarea, and update the document to match. |
2251 // When something is selected, it is present in the textarea, and | 2384 // When something is selected, it is present in the textarea, and |
2252 // selected (unless it is huge, in which case a placeholder is | 2385 // selected (unless it is huge, in which case a placeholder is |
2253 // used). When nothing is selected, the cursor sits after previously | 2386 // used). When nothing is selected, the cursor sits after previously |
2254 // seen text (can be empty), which is stored in prevInput (we must | 2387 // seen text (can be empty), which is stored in prevInput (we must |
2255 // not reset the textarea when typing, because that breaks IME). | 2388 // not reset the textarea when typing, because that breaks IME). |
2256 function readInput(cm) { | 2389 function readInput(cm) { |
2257 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc
; | 2390 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc
; |
2258 // Since this is called a *lot*, try to bail out as cheaply as | 2391 // Since this is called a *lot*, try to bail out as cheaply as |
2259 // possible when it is clear that nothing happened. hasSelection | 2392 // possible when it is clear that nothing happened. hasSelection |
2260 // will be the case when there is a lot of text in the textarea, | 2393 // will be the case when there is a lot of text in the textarea, |
2261 // in which case reading its value would be expensive. | 2394 // in which case reading its value would be expensive. |
2262 if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(c
m) || cm.options.disableInput) | 2395 if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(c
m) || cm.options.disableInput) |
2263 return false; | 2396 return false; |
2264 // See paste handler for more on the fakedLastChar kludge | 2397 // See paste handler for more on the fakedLastChar kludge |
2265 if (cm.state.pasteIncoming && cm.state.fakedLastChar) { | 2398 if (cm.state.pasteIncoming && cm.state.fakedLastChar) { |
2266 input.value = input.value.substring(0, input.value.length - 1); | 2399 input.value = input.value.substring(0, input.value.length - 1); |
2267 cm.state.fakedLastChar = false; | 2400 cm.state.fakedLastChar = false; |
2268 } | 2401 } |
2269 var text = input.value; | 2402 var text = input.value; |
2270 // If nothing changed, bail. | 2403 // If nothing changed, bail. |
2271 if (text == prevInput && !cm.somethingSelected()) return false; | 2404 if (text == prevInput && !cm.somethingSelected()) return false; |
2272 // Work around nonsensical selection resetting in IE9/10 | 2405 // Work around nonsensical selection resetting in IE9/10, and |
2273 if (ie && ie_version >= 9 && cm.display.inputHasSelection === text) { | 2406 // inexplicable appearance of private area unicode characters on |
| 2407 // some key combos in Mac (#2689). |
| 2408 if (ie && ie_version >= 9 && cm.display.inputHasSelection === text || |
| 2409 mac && /[\uf700-\uf7ff]/.test(text)) { |
2274 resetInput(cm); | 2410 resetInput(cm); |
2275 return false; | 2411 return false; |
2276 } | 2412 } |
2277 | 2413 |
2278 var withOp = !cm.curOp; | 2414 var withOp = !cm.curOp; |
2279 if (withOp) startOperation(cm); | 2415 if (withOp) startOperation(cm); |
2280 cm.display.shift = false; | 2416 cm.display.shift = false; |
2281 | 2417 |
2282 if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu
&& !prevInput) | 2418 if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu
&& !prevInput) |
2283 prevInput = "\u200b"; | 2419 prevInput = "\u200b"; |
2284 // Find the part of the input that is actually new | 2420 // Find the part of the input that is actually new |
2285 var same = 0, l = Math.min(prevInput.length, text.length); | 2421 var same = 0, l = Math.min(prevInput.length, text.length); |
2286 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++sa
me; | 2422 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++sa
me; |
2287 var inserted = text.slice(same), textLines = splitLines(inserted); | 2423 var inserted = text.slice(same), textLines = splitLines(inserted); |
2288 | 2424 |
2289 // When pasing N lines into N selections, insert one line per selection | 2425 // When pasing N lines into N selections, insert one line per selection |
2290 var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.r
anges.length == textLines.length; | 2426 var multiPaste = null; |
| 2427 if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) { |
| 2428 if (lastCopied && lastCopied.join("\n") == inserted) |
| 2429 multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastC
opied, splitLines); |
| 2430 else if (textLines.length == doc.sel.ranges.length) |
| 2431 multiPaste = map(textLines, function(l) { return [l]; }); |
| 2432 } |
2291 | 2433 |
2292 // Normal behavior is to insert the new text into every selection | 2434 // Normal behavior is to insert the new text into every selection |
2293 for (var i = doc.sel.ranges.length - 1; i >= 0; i--) { | 2435 for (var i = doc.sel.ranges.length - 1; i >= 0; i--) { |
2294 var range = doc.sel.ranges[i]; | 2436 var range = doc.sel.ranges[i]; |
2295 var from = range.from(), to = range.to(); | 2437 var from = range.from(), to = range.to(); |
2296 // Handle deletion | 2438 // Handle deletion |
2297 if (same < prevInput.length) | 2439 if (same < prevInput.length) |
2298 from = Pos(from.line, from.ch - (prevInput.length - same)); | 2440 from = Pos(from.line, from.ch - (prevInput.length - same)); |
2299 // Handle overwrite | 2441 // Handle overwrite |
2300 else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming) | 2442 else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming) |
2301 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + ls
t(textLines).length)); | 2443 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + ls
t(textLines).length)); |
2302 var updateInput = cm.curOp.updateInput; | 2444 var updateInput = cm.curOp.updateInput; |
2303 var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] :
textLines, | 2445 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % m
ultiPaste.length] : textLines, |
2304 origin: cm.state.pasteIncoming ? "paste" : cm.state.cut
Incoming ? "cut" : "+input"}; | 2446 origin: cm.state.pasteIncoming ? "paste" : cm.state.cut
Incoming ? "cut" : "+input"}; |
2305 makeChange(cm.doc, changeEvent); | 2447 makeChange(cm.doc, changeEvent); |
2306 signalLater(cm, "inputRead", cm, changeEvent); | 2448 signalLater(cm, "inputRead", cm, changeEvent); |
2307 // When an 'electric' character is inserted, immediately trigger a reinden
t | 2449 // When an 'electric' character is inserted, immediately trigger a reinden
t |
2308 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && | 2450 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && |
2309 cm.options.smartIndent && range.head.ch < 100 && | 2451 cm.options.smartIndent && range.head.ch < 100 && |
2310 (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) { | 2452 (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) { |
2311 var mode = cm.getModeAt(range.head); | 2453 var mode = cm.getModeAt(range.head); |
2312 if (mode.electricChars) { | 2454 if (mode.electricChars) { |
2313 for (var j = 0; j < mode.electricChars.length; j++) | 2455 for (var j = 0; j < mode.electricChars.length; j++) |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2414 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); | 2556 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); |
2415 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); | 2557 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); |
2416 | 2558 |
2417 // Prevent clicks in the scrollbars from killing focus | 2559 // Prevent clicks in the scrollbars from killing focus |
2418 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm),
0); } | 2560 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm),
0); } |
2419 on(d.scrollbarH, "mousedown", reFocus); | 2561 on(d.scrollbarH, "mousedown", reFocus); |
2420 on(d.scrollbarV, "mousedown", reFocus); | 2562 on(d.scrollbarV, "mousedown", reFocus); |
2421 // Prevent wrapper from ever scrolling | 2563 // Prevent wrapper from ever scrolling |
2422 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL
eft = 0; }); | 2564 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL
eft = 0; }); |
2423 | 2565 |
2424 on(d.input, "keyup", operation(cm, onKeyUp)); | 2566 on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); }); |
2425 on(d.input, "input", function() { | 2567 on(d.input, "input", function() { |
2426 if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inpu
tHasSelection = null; | 2568 if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inpu
tHasSelection = null; |
2427 fastPoll(cm); | 2569 fastPoll(cm); |
2428 }); | 2570 }); |
2429 on(d.input, "keydown", operation(cm, onKeyDown)); | 2571 on(d.input, "keydown", operation(cm, onKeyDown)); |
2430 on(d.input, "keypress", operation(cm, onKeyPress)); | 2572 on(d.input, "keypress", operation(cm, onKeyPress)); |
2431 on(d.input, "focus", bind(onFocus, cm)); | 2573 on(d.input, "focus", bind(onFocus, cm)); |
2432 on(d.input, "blur", bind(onBlur, cm)); | 2574 on(d.input, "blur", bind(onBlur, cm)); |
2433 | 2575 |
2434 function drag_(e) { | 2576 function drag_(e) { |
(...skipping 25 matching lines...) Expand all Loading... |
2460 d.input.selectionEnd = end; | 2602 d.input.selectionEnd = end; |
2461 d.input.selectionStart = start; | 2603 d.input.selectionStart = start; |
2462 cm.state.fakedLastChar = true; | 2604 cm.state.fakedLastChar = true; |
2463 } | 2605 } |
2464 cm.state.pasteIncoming = true; | 2606 cm.state.pasteIncoming = true; |
2465 fastPoll(cm); | 2607 fastPoll(cm); |
2466 }); | 2608 }); |
2467 | 2609 |
2468 function prepareCopyCut(e) { | 2610 function prepareCopyCut(e) { |
2469 if (cm.somethingSelected()) { | 2611 if (cm.somethingSelected()) { |
| 2612 lastCopied = cm.getSelections(); |
2470 if (d.inaccurateSelection) { | 2613 if (d.inaccurateSelection) { |
2471 d.prevInput = ""; | 2614 d.prevInput = ""; |
2472 d.inaccurateSelection = false; | 2615 d.inaccurateSelection = false; |
2473 d.input.value = cm.getSelection(); | 2616 d.input.value = lastCopied.join("\n"); |
2474 selectInput(d.input); | 2617 selectInput(d.input); |
2475 } | 2618 } |
2476 } else { | 2619 } else { |
2477 var text = "", ranges = []; | 2620 var text = [], ranges = []; |
2478 for (var i = 0; i < cm.doc.sel.ranges.length; i++) { | 2621 for (var i = 0; i < cm.doc.sel.ranges.length; i++) { |
2479 var line = cm.doc.sel.ranges[i].head.line; | 2622 var line = cm.doc.sel.ranges[i].head.line; |
2480 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; | 2623 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; |
2481 ranges.push(lineRange); | 2624 ranges.push(lineRange); |
2482 text += cm.getRange(lineRange.anchor, lineRange.head); | 2625 text.push(cm.getRange(lineRange.anchor, lineRange.head)); |
2483 } | 2626 } |
2484 if (e.type == "cut") { | 2627 if (e.type == "cut") { |
2485 cm.setSelections(ranges, null, sel_dontScroll); | 2628 cm.setSelections(ranges, null, sel_dontScroll); |
2486 } else { | 2629 } else { |
2487 d.prevInput = ""; | 2630 d.prevInput = ""; |
2488 d.input.value = text; | 2631 d.input.value = text.join("\n"); |
2489 selectInput(d.input); | 2632 selectInput(d.input); |
2490 } | 2633 } |
| 2634 lastCopied = text; |
2491 } | 2635 } |
2492 if (e.type == "cut") cm.state.cutIncoming = true; | 2636 if (e.type == "cut") cm.state.cutIncoming = true; |
2493 } | 2637 } |
2494 on(d.input, "cut", prepareCopyCut); | 2638 on(d.input, "cut", prepareCopyCut); |
2495 on(d.input, "copy", prepareCopyCut); | 2639 on(d.input, "copy", prepareCopyCut); |
2496 | 2640 |
2497 // Needed to handle Tab key in KHTML | 2641 // Needed to handle Tab key in KHTML |
2498 if (khtml) on(d.sizer, "mouseup", function() { | 2642 if (khtml) on(d.sizer, "mouseup", function() { |
2499 if (activeElt() == d.input) d.input.blur(); | 2643 if (activeElt() == d.input) d.input.blur(); |
2500 focusInput(cm); | 2644 focusInput(cm); |
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2878 } | 3022 } |
2879 } | 3023 } |
2880 | 3024 |
2881 // SCROLL EVENTS | 3025 // SCROLL EVENTS |
2882 | 3026 |
2883 // Sync the scrollable area and scrollbars, ensure the viewport | 3027 // Sync the scrollable area and scrollbars, ensure the viewport |
2884 // covers the visible area. | 3028 // covers the visible area. |
2885 function setScrollTop(cm, val) { | 3029 function setScrollTop(cm, val) { |
2886 if (Math.abs(cm.doc.scrollTop - val) < 2) return; | 3030 if (Math.abs(cm.doc.scrollTop - val) < 2) return; |
2887 cm.doc.scrollTop = val; | 3031 cm.doc.scrollTop = val; |
2888 if (!gecko) updateDisplay(cm, {top: val}); | 3032 if (!gecko) updateDisplaySimple(cm, {top: val}); |
2889 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va
l; | 3033 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va
l; |
2890 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop
= val; | 3034 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop
= val; |
2891 if (gecko) updateDisplay(cm); | 3035 if (gecko) updateDisplaySimple(cm); |
2892 startWorker(cm, 100); | 3036 startWorker(cm, 100); |
2893 } | 3037 } |
2894 // Sync scroller and scrollbar, ensure the gutter elements are | 3038 // Sync scroller and scrollbar, ensure the gutter elements are |
2895 // aligned. | 3039 // aligned. |
2896 function setScrollLeft(cm, val, isScroller) { | 3040 function setScrollLeft(cm, val, isScroller) { |
2897 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val
) < 2) return; | 3041 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val
) < 2) return; |
2898 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.cl
ientWidth); | 3042 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.cl
ientWidth); |
2899 cm.doc.scrollLeft = val; | 3043 cm.doc.scrollLeft = val; |
2900 alignHorizontally(cm); | 3044 alignHorizontally(cm); |
2901 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft =
val; | 3045 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft =
val; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2964 return; | 3108 return; |
2965 } | 3109 } |
2966 | 3110 |
2967 // 'Project' the visible viewport to cover the area that is being | 3111 // 'Project' the visible viewport to cover the area that is being |
2968 // scrolled into view (if we know enough to estimate it). | 3112 // scrolled into view (if we know enough to estimate it). |
2969 if (dy && wheelPixelsPerUnit != null) { | 3113 if (dy && wheelPixelsPerUnit != null) { |
2970 var pixels = dy * wheelPixelsPerUnit; | 3114 var pixels = dy * wheelPixelsPerUnit; |
2971 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; | 3115 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; |
2972 if (pixels < 0) top = Math.max(0, top + pixels - 50); | 3116 if (pixels < 0) top = Math.max(0, top + pixels - 50); |
2973 else bot = Math.min(cm.doc.height, bot + pixels + 50); | 3117 else bot = Math.min(cm.doc.height, bot + pixels + 50); |
2974 updateDisplay(cm, {top: top, bottom: bot}); | 3118 updateDisplaySimple(cm, {top: top, bottom: bot}); |
2975 } | 3119 } |
2976 | 3120 |
2977 if (wheelSamples < 20) { | 3121 if (wheelSamples < 20) { |
2978 if (display.wheelStartX == null) { | 3122 if (display.wheelStartX == null) { |
2979 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.sc
rollTop; | 3123 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.sc
rollTop; |
2980 display.wheelDX = dx; display.wheelDY = dy; | 3124 display.wheelDX = dx; display.wheelDY = dy; |
2981 setTimeout(function() { | 3125 setTimeout(function() { |
2982 if (display.wheelStartX == null) return; | 3126 if (display.wheelStartX == null) return; |
2983 var movedX = scroll.scrollLeft - display.wheelStartX; | 3127 var movedX = scroll.scrollLeft - display.wheelStartX; |
2984 var movedY = scroll.scrollTop - display.wheelStartY; | 3128 var movedY = scroll.scrollTop - display.wheelStartY; |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3107 rmClass(lineDiv, "CodeMirror-crosshair"); | 3251 rmClass(lineDiv, "CodeMirror-crosshair"); |
3108 off(document, "keyup", up); | 3252 off(document, "keyup", up); |
3109 off(document, "mouseover", up); | 3253 off(document, "mouseover", up); |
3110 } | 3254 } |
3111 } | 3255 } |
3112 on(document, "keyup", up); | 3256 on(document, "keyup", up); |
3113 on(document, "mouseover", up); | 3257 on(document, "mouseover", up); |
3114 } | 3258 } |
3115 | 3259 |
3116 function onKeyUp(e) { | 3260 function onKeyUp(e) { |
3117 if (signalDOMEvent(this, e)) return; | |
3118 if (e.keyCode == 16) this.doc.sel.shift = false; | 3261 if (e.keyCode == 16) this.doc.sel.shift = false; |
| 3262 signalDOMEvent(this, e); |
3119 } | 3263 } |
3120 | 3264 |
3121 function onKeyPress(e) { | 3265 function onKeyPress(e) { |
3122 var cm = this; | 3266 var cm = this; |
3123 if (signalDOMEvent(cm, e) || e.ctrlKey || mac && e.metaKey) return; | 3267 if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) ret
urn; |
3124 var keyCode = e.keyCode, charCode = e.charCode; | 3268 var keyCode = e.keyCode, charCode = e.charCode; |
3125 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe
fault(e); return;} | 3269 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe
fault(e); return;} |
3126 if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm
, e)) return; | 3270 if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm
, e)) return; |
3127 var ch = String.fromCharCode(charCode == null ? keyCode : charCode); | 3271 var ch = String.fromCharCode(charCode == null ? keyCode : charCode); |
3128 if (handleCharBinding(cm, e, ch)) return; | 3272 if (handleCharBinding(cm, e, ch)) return; |
3129 if (ie && ie_version >= 9) cm.display.inputHasSelection = null; | 3273 if (ie && ie_version >= 9) cm.display.inputHasSelection = null; |
3130 fastPoll(cm); | 3274 fastPoll(cm); |
3131 } | 3275 } |
3132 | 3276 |
3133 // FOCUS/BLUR EVENTS | 3277 // FOCUS/BLUR EVENTS |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3177 var reset = cm.options.resetSelectionOnContextMenu; | 3321 var reset = cm.options.resetSelectionOnContextMenu; |
3178 if (reset && cm.doc.sel.contains(pos) == -1) | 3322 if (reset && cm.doc.sel.contains(pos) == -1) |
3179 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); | 3323 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); |
3180 | 3324 |
3181 var oldCSS = display.input.style.cssText; | 3325 var oldCSS = display.input.style.cssText; |
3182 display.inputDiv.style.position = "absolute"; | 3326 display.inputDiv.style.position = "absolute"; |
3183 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t
op: " + (e.clientY - 5) + | 3327 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t
op: " + (e.clientY - 5) + |
3184 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " + | 3328 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " + |
3185 (ie ? "rgba(255, 255, 255, .05)" : "transparent") + | 3329 (ie ? "rgba(255, 255, 255, .05)" : "transparent") + |
3186 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacit
y: .05; filter: alpha(opacity=5);"; | 3330 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacit
y: .05; filter: alpha(opacity=5);"; |
| 3331 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2
712) |
3187 focusInput(cm); | 3332 focusInput(cm); |
| 3333 if (webkit) window.scrollTo(null, oldScrollY); |
3188 resetInput(cm); | 3334 resetInput(cm); |
3189 // Adds "Select all" to context menu in FF | 3335 // Adds "Select all" to context menu in FF |
3190 if (!cm.somethingSelected()) display.input.value = display.prevInput = " "; | 3336 if (!cm.somethingSelected()) display.input.value = display.prevInput = " "; |
3191 display.selForContextMenu = cm.doc.sel; | 3337 display.selForContextMenu = cm.doc.sel; |
3192 clearTimeout(display.detectingSelectAll); | 3338 clearTimeout(display.detectingSelectAll); |
3193 | 3339 |
3194 // Select-all will be greyed out if there's nothing to select, so | 3340 // Select-all will be greyed out if there's nothing to select, so |
3195 // this adds a zero-width space so that we can later check whether | 3341 // this adds a zero-width space so that we can later check whether |
3196 // it got selected. | 3342 // it got selected. |
3197 function prepareSelectAllHack() { | 3343 function prepareSelectAllHack() { |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3407 for (var i = event.changes.length - 1; i >= 0; --i) { | 3553 for (var i = event.changes.length - 1; i >= 0; --i) { |
3408 var change = event.changes[i]; | 3554 var change = event.changes[i]; |
3409 change.origin = type; | 3555 change.origin = type; |
3410 if (filter && !filterChange(doc, change, false)) { | 3556 if (filter && !filterChange(doc, change, false)) { |
3411 source.length = 0; | 3557 source.length = 0; |
3412 return; | 3558 return; |
3413 } | 3559 } |
3414 | 3560 |
3415 antiChanges.push(historyChangeFromChange(doc, change)); | 3561 antiChanges.push(historyChangeFromChange(doc, change)); |
3416 | 3562 |
3417 var after = i ? computeSelAfterChange(doc, change, null) : lst(source); | 3563 var after = i ? computeSelAfterChange(doc, change) : lst(source); |
3418 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); | 3564 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); |
3419 if (!i && doc.cm) doc.cm.scrollIntoView(change); | 3565 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(
change)}); |
3420 var rebased = []; | 3566 var rebased = []; |
3421 | 3567 |
3422 // Propagate to the linked documents | 3568 // Propagate to the linked documents |
3423 linkedDocs(doc, function(doc, sharedHist) { | 3569 linkedDocs(doc, function(doc, sharedHist) { |
3424 if (!sharedHist && indexOf(rebased, doc.history) == -1) { | 3570 if (!sharedHist && indexOf(rebased, doc.history) == -1) { |
3425 rebaseHist(doc.history, change); | 3571 rebaseHist(doc.history, change); |
3426 rebased.push(doc.history); | 3572 rebased.push(doc.history); |
3427 } | 3573 } |
3428 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); | 3574 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); |
3429 }); | 3575 }); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3466 text: [lst(change.text)], origin: change.origin}; | 3612 text: [lst(change.text)], origin: change.origin}; |
3467 } | 3613 } |
3468 var last = doc.lastLine(); | 3614 var last = doc.lastLine(); |
3469 if (change.to.line > last) { | 3615 if (change.to.line > last) { |
3470 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length)
, | 3616 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length)
, |
3471 text: [change.text[0]], origin: change.origin}; | 3617 text: [change.text[0]], origin: change.origin}; |
3472 } | 3618 } |
3473 | 3619 |
3474 change.removed = getBetween(doc, change.from, change.to); | 3620 change.removed = getBetween(doc, change.from, change.to); |
3475 | 3621 |
3476 if (!selAfter) selAfter = computeSelAfterChange(doc, change, null); | 3622 if (!selAfter) selAfter = computeSelAfterChange(doc, change); |
3477 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); | 3623 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); |
3478 else updateDoc(doc, change, spans); | 3624 else updateDoc(doc, change, spans); |
3479 setSelectionNoUndo(doc, selAfter, sel_dontScroll); | 3625 setSelectionNoUndo(doc, selAfter, sel_dontScroll); |
3480 } | 3626 } |
3481 | 3627 |
3482 // Handle the interaction of a change to a document with the editor | 3628 // Handle the interaction of a change to a document with the editor |
3483 // that this document is part of. | 3629 // that this document is part of. |
3484 function makeChangeSingleDocInEditor(cm, change, spans) { | 3630 function makeChangeSingleDocInEditor(cm, change, spans) { |
3485 var doc = cm.doc, display = cm.display, from = change.from, to = change.to; | 3631 var doc = cm.doc, display = cm.display, from = change.from, to = change.to; |
3486 | 3632 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3598 | 3744 |
3599 // Calculate a new scroll position needed to scroll the given | 3745 // Calculate a new scroll position needed to scroll the given |
3600 // rectangle into view. Returns an object with scrollTop and | 3746 // rectangle into view. Returns an object with scrollTop and |
3601 // scrollLeft properties. When these are undefined, the | 3747 // scrollLeft properties. When these are undefined, the |
3602 // vertical/horizontal position does not need to be adjusted. | 3748 // vertical/horizontal position does not need to be adjusted. |
3603 function calculateScrollPos(cm, x1, y1, x2, y2) { | 3749 function calculateScrollPos(cm, x1, y1, x2, y2) { |
3604 var display = cm.display, snapMargin = textHeight(cm.display); | 3750 var display = cm.display, snapMargin = textHeight(cm.display); |
3605 if (y1 < 0) y1 = 0; | 3751 if (y1 < 0) y1 = 0; |
3606 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop
: display.scroller.scrollTop; | 3752 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop
: display.scroller.scrollTop; |
3607 var screen = display.scroller.clientHeight - scrollerCutOff, result = {}; | 3753 var screen = display.scroller.clientHeight - scrollerCutOff, result = {}; |
| 3754 if (y2 - y1 > screen) y2 = y1 + screen; |
3608 var docBottom = cm.doc.height + paddingVert(display); | 3755 var docBottom = cm.doc.height + paddingVert(display); |
3609 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; | 3756 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; |
3610 if (y1 < screentop) { | 3757 if (y1 < screentop) { |
3611 result.scrollTop = atTop ? 0 : y1; | 3758 result.scrollTop = atTop ? 0 : y1; |
3612 } else if (y2 > screentop + screen) { | 3759 } else if (y2 > screentop + screen) { |
3613 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); | 3760 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); |
3614 if (newTop != screentop) result.scrollTop = newTop; | 3761 if (newTop != screentop) result.scrollTop = newTop; |
3615 } | 3762 } |
3616 | 3763 |
3617 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLe
ft : display.scroller.scrollLeft; | 3764 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLe
ft : display.scroller.scrollLeft; |
3618 var screenw = display.scroller.clientWidth - scrollerCutOff; | 3765 var screenw = display.scroller.clientWidth - scrollerCutOff - display.gutter
s.offsetWidth; |
3619 x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth; | 3766 var tooWide = x2 - x1 > screenw; |
3620 var gutterw = display.gutters.offsetWidth; | 3767 if (tooWide) x2 = y1 + screen; |
3621 var atLeft = x1 < gutterw + 10; | 3768 if (x1 < 10) |
3622 if (x1 < screenleft + gutterw || atLeft) { | 3769 result.scrollLeft = 0; |
3623 if (atLeft) x1 = 0; | 3770 else if (x1 < screenleft) |
3624 result.scrollLeft = Math.max(0, x1 - 10 - gutterw); | 3771 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)); |
3625 } else if (x2 > screenw + screenleft - 3) { | 3772 else if (x2 > screenw + screenleft - 3) |
3626 result.scrollLeft = x2 + 10 - screenw; | 3773 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw; |
3627 } | 3774 |
3628 return result; | 3775 return result; |
3629 } | 3776 } |
3630 | 3777 |
3631 // Store a relative adjustment to the scroll position in the current | 3778 // Store a relative adjustment to the scroll position in the current |
3632 // operation (to be applied when the operation finishes). | 3779 // operation (to be applied when the operation finishes). |
3633 function addToScrollPos(cm, left, top) { | 3780 function addToScrollPos(cm, left, top) { |
3634 if (left != null || top != null) resolveScrollToPos(cm); | 3781 if (left != null || top != null) resolveScrollToPos(cm); |
3635 if (left != null) | 3782 if (left != null) |
3636 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : c
m.curOp.scrollLeft) + left; | 3783 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : c
m.curOp.scrollLeft) + left; |
3637 if (top != null) | 3784 if (top != null) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3673 // "add"/null, "subtract", or "prev". When aggressive is false | 3820 // "add"/null, "subtract", or "prev". When aggressive is false |
3674 // (typically set to true for forced single-line indents), empty | 3821 // (typically set to true for forced single-line indents), empty |
3675 // lines are not indented, and places where the mode returns Pass | 3822 // lines are not indented, and places where the mode returns Pass |
3676 // are left alone. | 3823 // are left alone. |
3677 function indentLine(cm, n, how, aggressive) { | 3824 function indentLine(cm, n, how, aggressive) { |
3678 var doc = cm.doc, state; | 3825 var doc = cm.doc, state; |
3679 if (how == null) how = "add"; | 3826 if (how == null) how = "add"; |
3680 if (how == "smart") { | 3827 if (how == "smart") { |
3681 // Fall back to "prev" when the mode doesn't have an indentation | 3828 // Fall back to "prev" when the mode doesn't have an indentation |
3682 // method. | 3829 // method. |
3683 if (!cm.doc.mode.indent) how = "prev"; | 3830 if (!doc.mode.indent) how = "prev"; |
3684 else state = getStateBefore(cm, n); | 3831 else state = getStateBefore(cm, n); |
3685 } | 3832 } |
3686 | 3833 |
3687 var tabSize = cm.options.tabSize; | 3834 var tabSize = cm.options.tabSize; |
3688 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
; | 3835 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
; |
3689 if (line.stateAfter) line.stateAfter = null; | 3836 if (line.stateAfter) line.stateAfter = null; |
3690 var curSpaceString = line.text.match(/^\s*/)[0], indentation; | 3837 var curSpaceString = line.text.match(/^\s*/)[0], indentation; |
3691 if (!aggressive && !/\S/.test(line.text)) { | 3838 if (!aggressive && !/\S/.test(line.text)) { |
3692 indentation = 0; | 3839 indentation = 0; |
3693 how = "not"; | 3840 how = "not"; |
3694 } else if (how == "smart") { | 3841 } else if (how == "smart") { |
3695 indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.len
gth), line.text); | 3842 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length
), line.text); |
3696 if (indentation == Pass) { | 3843 if (indentation == Pass || indentation > 150) { |
3697 if (!aggressive) return; | 3844 if (!aggressive) return; |
3698 how = "prev"; | 3845 how = "prev"; |
3699 } | 3846 } |
3700 } | 3847 } |
3701 if (how == "prev") { | 3848 if (how == "prev") { |
3702 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null,
tabSize); | 3849 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null,
tabSize); |
3703 else indentation = 0; | 3850 else indentation = 0; |
3704 } else if (how == "add") { | 3851 } else if (how == "add") { |
3705 indentation = curSpace + cm.options.indentUnit; | 3852 indentation = curSpace + cm.options.indentUnit; |
3706 } else if (how == "subtract") { | 3853 } else if (how == "subtract") { |
3707 indentation = curSpace - cm.options.indentUnit; | 3854 indentation = curSpace - cm.options.indentUnit; |
3708 } else if (typeof how == "number") { | 3855 } else if (typeof how == "number") { |
3709 indentation = curSpace + how; | 3856 indentation = curSpace + how; |
3710 } | 3857 } |
3711 indentation = Math.max(0, indentation); | 3858 indentation = Math.max(0, indentation); |
3712 | 3859 |
3713 var indentString = "", pos = 0; | 3860 var indentString = "", pos = 0; |
3714 if (cm.options.indentWithTabs) | 3861 if (cm.options.indentWithTabs) |
3715 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i
ndentString += "\t";} | 3862 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i
ndentString += "\t";} |
3716 if (pos < indentation) indentString += spaceStr(indentation - pos); | 3863 if (pos < indentation) indentString += spaceStr(indentation - pos); |
3717 | 3864 |
3718 if (indentString != curSpaceString) { | 3865 if (indentString != curSpaceString) { |
3719 replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length
), "+input"); | 3866 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length),
"+input"); |
3720 } else { | 3867 } else { |
3721 // Ensure that, if the cursor was in the whitespace at the start | 3868 // Ensure that, if the cursor was in the whitespace at the start |
3722 // of the line, it is moved to the end of that space. | 3869 // of the line, it is moved to the end of that space. |
3723 for (var i = 0; i < doc.sel.ranges.length; i++) { | 3870 for (var i = 0; i < doc.sel.ranges.length; i++) { |
3724 var range = doc.sel.ranges[i]; | 3871 var range = doc.sel.ranges[i]; |
3725 if (range.head.line == n && range.head.ch < curSpaceString.length) { | 3872 if (range.head.line == n && range.head.ch < curSpaceString.length) { |
3726 var pos = Pos(n, curSpaceString.length); | 3873 var pos = Pos(n, curSpaceString.length); |
3727 replaceOneSelection(doc, i, new Range(pos, pos)); | 3874 replaceOneSelection(doc, i, new Range(pos, pos)); |
3728 break; | 3875 break; |
3729 } | 3876 } |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3926 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; | 4073 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; |
3927 else dir = dir ? "add" : "subtract"; | 4074 else dir = dir ? "add" : "subtract"; |
3928 } | 4075 } |
3929 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); | 4076 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); |
3930 }), | 4077 }), |
3931 indentSelection: methodOp(function(how) { | 4078 indentSelection: methodOp(function(how) { |
3932 var ranges = this.doc.sel.ranges, end = -1; | 4079 var ranges = this.doc.sel.ranges, end = -1; |
3933 for (var i = 0; i < ranges.length; i++) { | 4080 for (var i = 0; i < ranges.length; i++) { |
3934 var range = ranges[i]; | 4081 var range = ranges[i]; |
3935 if (!range.empty()) { | 4082 if (!range.empty()) { |
3936 var start = Math.max(end, range.from().line); | 4083 var from = range.from(), to = range.to(); |
3937 var to = range.to(); | 4084 var start = Math.max(end, from.line); |
3938 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; | 4085 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; |
3939 for (var j = start; j < end; ++j) | 4086 for (var j = start; j < end; ++j) |
3940 indentLine(this, j, how); | 4087 indentLine(this, j, how); |
| 4088 var newRanges = this.doc.sel.ranges; |
| 4089 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].
from().ch > 0) |
| 4090 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()))
; |
3941 } else if (range.head.line > end) { | 4091 } else if (range.head.line > end) { |
3942 indentLine(this, range.head.line, how, true); | 4092 indentLine(this, range.head.line, how, true); |
3943 end = range.head.line; | 4093 end = range.head.line; |
3944 if (i == this.doc.sel.primIndex) ensureCursorVisible(this); | 4094 if (i == this.doc.sel.primIndex) ensureCursorVisible(this); |
3945 } | 4095 } |
3946 } | 4096 } |
3947 }), | 4097 }), |
3948 | 4098 |
3949 // Fetch the parser token for a given character. Useful for hacks | 4099 // Fetch the parser token for a given character. Useful for hacks |
3950 // that want to inspect the mode state (say, for completion). | 4100 // that want to inspect the mode state (say, for completion). |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4126 if (horiz == "left") left = 0; | 4276 if (horiz == "left") left = 0; |
4127 else if (horiz == "middle") left = (display.sizer.clientWidth - node.off
setWidth) / 2; | 4277 else if (horiz == "middle") left = (display.sizer.clientWidth - node.off
setWidth) / 2; |
4128 node.style.left = left + "px"; | 4278 node.style.left = left + "px"; |
4129 } | 4279 } |
4130 if (scroll) | 4280 if (scroll) |
4131 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs
etHeight); | 4281 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs
etHeight); |
4132 }, | 4282 }, |
4133 | 4283 |
4134 triggerOnKeyDown: methodOp(onKeyDown), | 4284 triggerOnKeyDown: methodOp(onKeyDown), |
4135 triggerOnKeyPress: methodOp(onKeyPress), | 4285 triggerOnKeyPress: methodOp(onKeyPress), |
4136 triggerOnKeyUp: methodOp(onKeyUp), | 4286 triggerOnKeyUp: onKeyUp, |
4137 | 4287 |
4138 execCommand: function(cmd) { | 4288 execCommand: function(cmd) { |
4139 if (commands.hasOwnProperty(cmd)) | 4289 if (commands.hasOwnProperty(cmd)) |
4140 return commands[cmd](this); | 4290 return commands[cmd](this); |
4141 }, | 4291 }, |
4142 | 4292 |
4143 findPosH: function(from, amount, unit, visually) { | 4293 findPosH: function(from, amount, unit, visually) { |
4144 var dir = 1; | 4294 var dir = 1; |
4145 if (amount < 0) { dir = -1; amount = -amount; } | 4295 if (amount < 0) { dir = -1; amount = -amount; } |
4146 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { | 4296 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4326 }, true); | 4476 }, true); |
4327 | 4477 |
4328 option("indentUnit", 2, loadMode, true); | 4478 option("indentUnit", 2, loadMode, true); |
4329 option("indentWithTabs", false); | 4479 option("indentWithTabs", false); |
4330 option("smartIndent", true); | 4480 option("smartIndent", true); |
4331 option("tabSize", 4, function(cm) { | 4481 option("tabSize", 4, function(cm) { |
4332 resetModeState(cm); | 4482 resetModeState(cm); |
4333 clearCaches(cm); | 4483 clearCaches(cm); |
4334 regChange(cm); | 4484 regChange(cm); |
4335 }, true); | 4485 }, true); |
4336 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, fun
ction(cm, val) { | 4486 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]
/g, function(cm, val) { |
4337 cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\
t"), "g"); | 4487 cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\
t"), "g"); |
4338 cm.refresh(); | 4488 cm.refresh(); |
4339 }, true); | 4489 }, true); |
4340 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {
cm.refresh();}, true); | 4490 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {
cm.refresh();}, true); |
4341 option("electricChars", true); | 4491 option("electricChars", true); |
4342 option("rtlMoveVisually", !windows); | 4492 option("rtlMoveVisually", !windows); |
4343 option("wholeLineUpdateBefore", true); | 4493 option("wholeLineUpdateBefore", true); |
4344 | 4494 |
4345 option("theme", "default", function(cm) { | 4495 option("theme", "default", function(cm) { |
4346 themeChanged(cm); | 4496 themeChanged(cm); |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4561 deleteNearSelection(cm, function(range) { | 4711 deleteNearSelection(cm, function(range) { |
4562 return {from: Pos(range.from().line, 0), | 4712 return {from: Pos(range.from().line, 0), |
4563 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; | 4713 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; |
4564 }); | 4714 }); |
4565 }, | 4715 }, |
4566 delLineLeft: function(cm) { | 4716 delLineLeft: function(cm) { |
4567 deleteNearSelection(cm, function(range) { | 4717 deleteNearSelection(cm, function(range) { |
4568 return {from: Pos(range.from().line, 0), to: range.from()}; | 4718 return {from: Pos(range.from().line, 0), to: range.from()}; |
4569 }); | 4719 }); |
4570 }, | 4720 }, |
| 4721 delWrappedLineLeft: function(cm) { |
| 4722 deleteNearSelection(cm, function(range) { |
| 4723 var top = cm.charCoords(range.head, "div").top + 5; |
| 4724 var leftPos = cm.coordsChar({left: 0, top: top}, "div"); |
| 4725 return {from: leftPos, to: range.from()}; |
| 4726 }); |
| 4727 }, |
| 4728 delWrappedLineRight: function(cm) { |
| 4729 deleteNearSelection(cm, function(range) { |
| 4730 var top = cm.charCoords(range.head, "div").top + 5; |
| 4731 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100
, top: top}, "div"); |
| 4732 return {from: range.from(), to: rightPos }; |
| 4733 }); |
| 4734 }, |
4571 undo: function(cm) {cm.undo();}, | 4735 undo: function(cm) {cm.undo();}, |
4572 redo: function(cm) {cm.redo();}, | 4736 redo: function(cm) {cm.redo();}, |
4573 undoSelection: function(cm) {cm.undoSelection();}, | 4737 undoSelection: function(cm) {cm.undoSelection();}, |
4574 redoSelection: function(cm) {cm.redoSelection();}, | 4738 redoSelection: function(cm) {cm.redoSelection();}, |
4575 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, | 4739 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, |
4576 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, | 4740 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, |
4577 goLineStart: function(cm) { | 4741 goLineStart: function(cm) { |
4578 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.li
ne); }, | 4742 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.li
ne); }, |
4579 {origin: "+move", bias: 1}); | 4743 {origin: "+move", bias: 1}); |
4580 }, | 4744 }, |
4581 goLineStartSmart: function(cm) { | 4745 goLineStartSmart: function(cm) { |
4582 cm.extendSelectionsBy(function(range) { | 4746 cm.extendSelectionsBy(function(range) { |
4583 var start = lineStart(cm, range.head.line); | 4747 return lineStartSmart(cm, range.head); |
4584 var line = cm.getLineHandle(start.line); | |
4585 var order = getOrder(line); | |
4586 if (!order || order[0].level == 0) { | |
4587 var firstNonWS = Math.max(0, line.text.search(/\S/)); | |
4588 var inWS = range.head.line == start.line && range.head.ch <= firstNonW
S && range.head.ch; | |
4589 return Pos(start.line, inWS ? 0 : firstNonWS); | |
4590 } | |
4591 return start; | |
4592 }, {origin: "+move", bias: 1}); | 4748 }, {origin: "+move", bias: 1}); |
4593 }, | 4749 }, |
4594 goLineEnd: function(cm) { | 4750 goLineEnd: function(cm) { |
4595 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line
); }, | 4751 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line
); }, |
4596 {origin: "+move", bias: -1}); | 4752 {origin: "+move", bias: -1}); |
4597 }, | 4753 }, |
4598 goLineRight: function(cm) { | 4754 goLineRight: function(cm) { |
4599 cm.extendSelectionsBy(function(range) { | 4755 cm.extendSelectionsBy(function(range) { |
4600 var top = cm.charCoords(range.head, "div").top + 5; | 4756 var top = cm.charCoords(range.head, "div").top + 5; |
4601 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: t
op}, "div"); | 4757 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: t
op}, "div"); |
4602 }, sel_move); | 4758 }, sel_move); |
4603 }, | 4759 }, |
4604 goLineLeft: function(cm) { | 4760 goLineLeft: function(cm) { |
4605 cm.extendSelectionsBy(function(range) { | 4761 cm.extendSelectionsBy(function(range) { |
4606 var top = cm.charCoords(range.head, "div").top + 5; | 4762 var top = cm.charCoords(range.head, "div").top + 5; |
4607 return cm.coordsChar({left: 0, top: top}, "div"); | 4763 return cm.coordsChar({left: 0, top: top}, "div"); |
4608 }, sel_move); | 4764 }, sel_move); |
4609 }, | 4765 }, |
| 4766 goLineLeftSmart: function(cm) { |
| 4767 cm.extendSelectionsBy(function(range) { |
| 4768 var top = cm.charCoords(range.head, "div").top + 5; |
| 4769 var pos = cm.coordsChar({left: 0, top: top}, "div"); |
| 4770 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm
, range.head); |
| 4771 return pos; |
| 4772 }, sel_move); |
| 4773 }, |
4610 goLineUp: function(cm) {cm.moveV(-1, "line");}, | 4774 goLineUp: function(cm) {cm.moveV(-1, "line");}, |
4611 goLineDown: function(cm) {cm.moveV(1, "line");}, | 4775 goLineDown: function(cm) {cm.moveV(1, "line");}, |
4612 goPageUp: function(cm) {cm.moveV(-1, "page");}, | 4776 goPageUp: function(cm) {cm.moveV(-1, "page");}, |
4613 goPageDown: function(cm) {cm.moveV(1, "page");}, | 4777 goPageDown: function(cm) {cm.moveV(1, "page");}, |
4614 goCharLeft: function(cm) {cm.moveH(-1, "char");}, | 4778 goCharLeft: function(cm) {cm.moveH(-1, "char");}, |
4615 goCharRight: function(cm) {cm.moveH(1, "char");}, | 4779 goCharRight: function(cm) {cm.moveH(1, "char");}, |
4616 goColumnLeft: function(cm) {cm.moveH(-1, "column");}, | 4780 goColumnLeft: function(cm) {cm.moveH(-1, "column");}, |
4617 goColumnRight: function(cm) {cm.moveH(1, "column");}, | 4781 goColumnRight: function(cm) {cm.moveH(1, "column");}, |
4618 goWordLeft: function(cm) {cm.moveH(-1, "word");}, | 4782 goWordLeft: function(cm) {cm.moveH(-1, "word");}, |
4619 goGroupRight: function(cm) {cm.moveH(1, "group");}, | 4783 goGroupRight: function(cm) {cm.moveH(1, "group");}, |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4698 "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd",
"Ctrl-Down": "goDocEnd", | 4862 "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd",
"Ctrl-Down": "goDocEnd", |
4699 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLin
eStart", "Alt-Right": "goLineEnd", | 4863 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLin
eStart", "Alt-Right": "goLineEnd", |
4700 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S"
: "save", "Ctrl-F": "find", | 4864 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S"
: "save", "Ctrl-F": "find", |
4701 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace",
"Shift-Ctrl-R": "replaceAll", | 4865 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace",
"Shift-Ctrl-R": "replaceAll", |
4702 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", | 4866 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", |
4703 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe
lection", | 4867 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe
lection", |
4704 fallthrough: "basic" | 4868 fallthrough: "basic" |
4705 }; | 4869 }; |
4706 keyMap.macDefault = { | 4870 keyMap.macDefault = { |
4707 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z":
"redo", "Cmd-Y": "redo", | 4871 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z":
"redo", "Cmd-Y": "redo", |
4708 "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-
Left": "goGroupLeft", | 4872 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cm
d-Down": "goDocEnd", "Alt-Left": "goGroupLeft", |
4709 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLine
End", "Alt-Backspace": "delGroupBefore", | 4873 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineR
ight", "Alt-Backspace": "delGroupBefore", |
4710 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S
": "save", "Cmd-F": "find", | 4874 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S
": "save", "Cmd-F": "find", |
4711 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi
ft-Cmd-Alt-F": "replaceAll", | 4875 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi
ft-Cmd-Alt-F": "replaceAll", |
4712 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft"
, | 4876 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLi
neLeft", "Cmd-Delete": "delWrappedLineRight", |
4713 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", | 4877 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", |
4714 fallthrough: ["basic", "emacsy"] | 4878 fallthrough: ["basic", "emacsy"] |
4715 }; | 4879 }; |
4716 // Very basic readline/emacs-style bindings, which are standard on Mac. | 4880 // Very basic readline/emacs-style bindings, which are standard on Mac. |
4717 keyMap.emacsy = { | 4881 keyMap.emacsy = { |
4718 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl
-N": "goLineDown", | 4882 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl
-N": "goLineDown", |
4719 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr
l-E": "goLineEnd", | 4883 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr
l-E": "goLineEnd", |
4720 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter"
, "Ctrl-H": "delCharBefore", | 4884 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter"
, "Ctrl-H": "delCharBefore", |
4721 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi
ne", "Ctrl-T": "transposeChars" | 4885 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi
ne", "Ctrl-T": "transposeChars" |
4722 }; | 4886 }; |
(...skipping 1614 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6337 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) +
+undone; | 6501 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) +
+undone; |
6338 return {undo: done, redo: undone}; | 6502 return {undo: done, redo: undone}; |
6339 }, | 6503 }, |
6340 clearHistory: function() {this.history = new History(this.history.maxGenerat
ion);}, | 6504 clearHistory: function() {this.history = new History(this.history.maxGenerat
ion);}, |
6341 | 6505 |
6342 markClean: function() { | 6506 markClean: function() { |
6343 this.cleanGeneration = this.changeGeneration(true); | 6507 this.cleanGeneration = this.changeGeneration(true); |
6344 }, | 6508 }, |
6345 changeGeneration: function(forceSplit) { | 6509 changeGeneration: function(forceSplit) { |
6346 if (forceSplit) | 6510 if (forceSplit) |
6347 this.history.lastOp = this.history.lastOrigin = null; | 6511 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin =
null; |
6348 return this.history.generation; | 6512 return this.history.generation; |
6349 }, | 6513 }, |
6350 isClean: function (gen) { | 6514 isClean: function (gen) { |
6351 return this.history.generation == (gen || this.cleanGeneration); | 6515 return this.history.generation == (gen || this.cleanGeneration); |
6352 }, | 6516 }, |
6353 | 6517 |
6354 getHistory: function() { | 6518 getHistory: function() { |
6355 return {done: copyHistoryArray(this.history.done), | 6519 return {done: copyHistoryArray(this.history.done), |
6356 undone: copyHistoryArray(this.history.undone)}; | 6520 undone: copyHistoryArray(this.history.undone)}; |
6357 }, | 6521 }, |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6654 | 6818 |
6655 function History(startGen) { | 6819 function History(startGen) { |
6656 // Arrays of change events and selections. Doing something adds an | 6820 // Arrays of change events and selections. Doing something adds an |
6657 // event to done and clears undo. Undoing moves events from done | 6821 // event to done and clears undo. Undoing moves events from done |
6658 // to undone, redoing moves them in the other direction. | 6822 // to undone, redoing moves them in the other direction. |
6659 this.done = []; this.undone = []; | 6823 this.done = []; this.undone = []; |
6660 this.undoDepth = Infinity; | 6824 this.undoDepth = Infinity; |
6661 // Used to track when changes can be merged into a single undo | 6825 // Used to track when changes can be merged into a single undo |
6662 // event | 6826 // event |
6663 this.lastModTime = this.lastSelTime = 0; | 6827 this.lastModTime = this.lastSelTime = 0; |
6664 this.lastOp = null; | 6828 this.lastOp = this.lastSelOp = null; |
6665 this.lastOrigin = this.lastSelOrigin = null; | 6829 this.lastOrigin = this.lastSelOrigin = null; |
6666 // Used by the isClean() method | 6830 // Used by the isClean() method |
6667 this.generation = this.maxGeneration = startGen || 1; | 6831 this.generation = this.maxGeneration = startGen || 1; |
6668 } | 6832 } |
6669 | 6833 |
6670 // Create a history change event from an updateDoc-style change | 6834 // Create a history change event from an updateDoc-style change |
6671 // object. | 6835 // object. |
6672 function historyChangeFromChange(doc, change) { | 6836 function historyChangeFromChange(doc, change) { |
6673 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: g
etBetween(doc, change.from, change.to)}; | 6837 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: g
etBetween(doc, change.from, change.to)}; |
6674 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); | 6838 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6732 generation: hist.generation}; | 6896 generation: hist.generation}; |
6733 hist.done.push(cur); | 6897 hist.done.push(cur); |
6734 while (hist.done.length > hist.undoDepth) { | 6898 while (hist.done.length > hist.undoDepth) { |
6735 hist.done.shift(); | 6899 hist.done.shift(); |
6736 if (!hist.done[0].ranges) hist.done.shift(); | 6900 if (!hist.done[0].ranges) hist.done.shift(); |
6737 } | 6901 } |
6738 } | 6902 } |
6739 hist.done.push(selAfter); | 6903 hist.done.push(selAfter); |
6740 hist.generation = ++hist.maxGeneration; | 6904 hist.generation = ++hist.maxGeneration; |
6741 hist.lastModTime = hist.lastSelTime = time; | 6905 hist.lastModTime = hist.lastSelTime = time; |
6742 hist.lastOp = opId; | 6906 hist.lastOp = hist.lastSelOp = opId; |
6743 hist.lastOrigin = hist.lastSelOrigin = change.origin; | 6907 hist.lastOrigin = hist.lastSelOrigin = change.origin; |
6744 | 6908 |
6745 if (!last) signal(doc, "historyAdded"); | 6909 if (!last) signal(doc, "historyAdded"); |
6746 } | 6910 } |
6747 | 6911 |
6748 function selectionEventCanBeMerged(doc, origin, prev, sel) { | 6912 function selectionEventCanBeMerged(doc, origin, prev, sel) { |
6749 var ch = origin.charAt(0); | 6913 var ch = origin.charAt(0); |
6750 return ch == "*" || | 6914 return ch == "*" || |
6751 ch == "+" && | 6915 ch == "+" && |
6752 prev.ranges.length == sel.ranges.length && | 6916 prev.ranges.length == sel.ranges.length && |
6753 prev.somethingSelected() == sel.somethingSelected() && | 6917 prev.somethingSelected() == sel.somethingSelected() && |
6754 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEven
tDelay : 500); | 6918 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEven
tDelay : 500); |
6755 } | 6919 } |
6756 | 6920 |
6757 // Called whenever the selection changes, sets the new selection as | 6921 // Called whenever the selection changes, sets the new selection as |
6758 // the pending selection in the history, and pushes the old pending | 6922 // the pending selection in the history, and pushes the old pending |
6759 // selection into the 'done' array when it was significantly | 6923 // selection into the 'done' array when it was significantly |
6760 // different (in number of selected ranges, emptiness, or time). | 6924 // different (in number of selected ranges, emptiness, or time). |
6761 function addSelectionToHistory(doc, sel, opId, options) { | 6925 function addSelectionToHistory(doc, sel, opId, options) { |
6762 var hist = doc.history, origin = options && options.origin; | 6926 var hist = doc.history, origin = options && options.origin; |
6763 | 6927 |
6764 // A new event is started when the previous origin does not match | 6928 // A new event is started when the previous origin does not match |
6765 // the current, or the origins don't allow matching. Origins | 6929 // the current, or the origins don't allow matching. Origins |
6766 // starting with * are always merged, those starting with + are | 6930 // starting with * are always merged, those starting with + are |
6767 // merged when similar and close together in time. | 6931 // merged when similar and close together in time. |
6768 if (opId == hist.lastOp || | 6932 if (opId == hist.lastSelOp || |
6769 (origin && hist.lastSelOrigin == origin && | 6933 (origin && hist.lastSelOrigin == origin && |
6770 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || | 6934 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || |
6771 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) | 6935 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) |
6772 hist.done[hist.done.length - 1] = sel; | 6936 hist.done[hist.done.length - 1] = sel; |
6773 else | 6937 else |
6774 pushSelectionToHistory(sel, hist.done); | 6938 pushSelectionToHistory(sel, hist.done); |
6775 | 6939 |
6776 hist.lastSelTime = +new Date; | 6940 hist.lastSelTime = +new Date; |
6777 hist.lastSelOrigin = origin; | 6941 hist.lastSelOrigin = origin; |
6778 hist.lastOp = opId; | 6942 hist.lastSelOp = opId; |
6779 if (options && options.clearRedo !== false) | 6943 if (options && options.clearRedo !== false) |
6780 clearSelectionEvents(hist.undone); | 6944 clearSelectionEvents(hist.undone); |
6781 } | 6945 } |
6782 | 6946 |
6783 function pushSelectionToHistory(sel, dest) { | 6947 function pushSelectionToHistory(sel, dest) { |
6784 var top = lst(dest); | 6948 var top = lst(dest); |
6785 if (!(top && top.ranges && top.equals(sel))) | 6949 if (!(top && top.ranges && top.equals(sel))) |
6786 dest.push(sel); | 6950 dest.push(sel); |
6787 } | 6951 } |
6788 | 6952 |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6953 } | 7117 } |
6954 }; | 7118 }; |
6955 | 7119 |
6956 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { | 7120 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { |
6957 var arr = emitter._handlers && emitter._handlers[type]; | 7121 var arr = emitter._handlers && emitter._handlers[type]; |
6958 if (!arr) return; | 7122 if (!arr) return; |
6959 var args = Array.prototype.slice.call(arguments, 2); | 7123 var args = Array.prototype.slice.call(arguments, 2); |
6960 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); | 7124 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); |
6961 }; | 7125 }; |
6962 | 7126 |
| 7127 var orphanDelayedCallbacks = null; |
| 7128 |
6963 // Often, we want to signal events at a point where we are in the | 7129 // Often, we want to signal events at a point where we are in the |
6964 // middle of some work, but don't want the handler to start calling | 7130 // middle of some work, but don't want the handler to start calling |
6965 // other methods on the editor, which might be in an inconsistent | 7131 // other methods on the editor, which might be in an inconsistent |
6966 // state or simply not expect any other events to happen. | 7132 // state or simply not expect any other events to happen. |
6967 // signalLater looks whether there are any handlers, and schedules | 7133 // signalLater looks whether there are any handlers, and schedules |
6968 // them to be executed when the last operation ends, or, if no | 7134 // them to be executed when the last operation ends, or, if no |
6969 // operation is active, when a timeout fires. | 7135 // operation is active, when a timeout fires. |
6970 var delayedCallbacks, delayedCallbackDepth = 0; | |
6971 function signalLater(emitter, type /*, values...*/) { | 7136 function signalLater(emitter, type /*, values...*/) { |
6972 var arr = emitter._handlers && emitter._handlers[type]; | 7137 var arr = emitter._handlers && emitter._handlers[type]; |
6973 if (!arr) return; | 7138 if (!arr) return; |
6974 var args = Array.prototype.slice.call(arguments, 2); | 7139 var args = Array.prototype.slice.call(arguments, 2), list; |
6975 if (!delayedCallbacks) { | 7140 if (operationGroup) { |
6976 ++delayedCallbackDepth; | 7141 list = operationGroup.delayedCallbacks; |
6977 delayedCallbacks = []; | 7142 } else if (orphanDelayedCallbacks) { |
6978 setTimeout(fireDelayed, 0); | 7143 list = orphanDelayedCallbacks; |
| 7144 } else { |
| 7145 list = orphanDelayedCallbacks = []; |
| 7146 setTimeout(fireOrphanDelayed, 0); |
6979 } | 7147 } |
6980 function bnd(f) {return function(){f.apply(null, args);};}; | 7148 function bnd(f) {return function(){f.apply(null, args);};}; |
6981 for (var i = 0; i < arr.length; ++i) | 7149 for (var i = 0; i < arr.length; ++i) |
6982 delayedCallbacks.push(bnd(arr[i])); | 7150 list.push(bnd(arr[i])); |
6983 } | 7151 } |
6984 | 7152 |
6985 function fireDelayed() { | 7153 function fireOrphanDelayed() { |
6986 --delayedCallbackDepth; | 7154 var delayed = orphanDelayedCallbacks; |
6987 var delayed = delayedCallbacks; | 7155 orphanDelayedCallbacks = null; |
6988 delayedCallbacks = null; | |
6989 for (var i = 0; i < delayed.length; ++i) delayed[i](); | 7156 for (var i = 0; i < delayed.length; ++i) delayed[i](); |
6990 } | 7157 } |
6991 | 7158 |
6992 // The DOM events that CodeMirror handles can be overridden by | 7159 // The DOM events that CodeMirror handles can be overridden by |
6993 // registering a (non-DOM) handler on the editor for the event name, | 7160 // registering a (non-DOM) handler on the editor for the event name, |
6994 // and preventDefault-ing the event in that handler. | 7161 // and preventDefault-ing the event in that handler. |
6995 function signalDOMEvent(cm, e, override) { | 7162 function signalDOMEvent(cm, e, override) { |
6996 signal(cm, override || e.type, cm, e); | 7163 signal(cm, override || e.type, cm, e); |
6997 return e_defaultPrevented(e) || e.codemirrorIgnore; | 7164 return e_defaultPrevented(e) || e.codemirrorIgnore; |
6998 } | 7165 } |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7324 return range.compareEndPoints("StartToEnd", range) != 0; | 7491 return range.compareEndPoints("StartToEnd", range) != 0; |
7325 }; | 7492 }; |
7326 | 7493 |
7327 var hasCopyEvent = (function() { | 7494 var hasCopyEvent = (function() { |
7328 var e = elt("div"); | 7495 var e = elt("div"); |
7329 if ("oncopy" in e) return true; | 7496 if ("oncopy" in e) return true; |
7330 e.setAttribute("oncopy", "return;"); | 7497 e.setAttribute("oncopy", "return;"); |
7331 return typeof e.oncopy == "function"; | 7498 return typeof e.oncopy == "function"; |
7332 })(); | 7499 })(); |
7333 | 7500 |
| 7501 var badZoomedRects = null; |
| 7502 function hasBadZoomedRects(measure) { |
| 7503 if (badZoomedRects != null) return badZoomedRects; |
| 7504 var node = removeChildrenAndAdd(measure, elt("span", "x")); |
| 7505 var normal = node.getBoundingClientRect(); |
| 7506 var fromRange = range(node, 0, 1).getBoundingClientRect(); |
| 7507 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1; |
| 7508 } |
| 7509 |
7334 // KEY NAMES | 7510 // KEY NAMES |
7335 | 7511 |
7336 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift"
, 17: "Ctrl", 18: "Alt", | 7512 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift"
, 17: "Ctrl", 18: "Alt", |
7337 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "Page
Up", 34: "PageDown", 35: "End", | 7513 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "Page
Up", 34: "PageDown", 35: "End", |
7338 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44:
"PrintScrn", 45: "Insert", | 7514 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44:
"PrintScrn", 45: "Insert", |
7339 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod
", 107: "=", 109: "-", 127: "Delete", | 7515 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod
", 107: "=", 109: "-", 127: "Delete", |
7340 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 19
1: "/", 192: "`", 219: "[", 220: "\\", | 7516 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 19
1: "/", 192: "`", 219: "[", 220: "\\", |
7341 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left",
63235: "Right", 63272: "Delete", | 7517 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left",
63235: "Right", 63272: "Delete", |
7342 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown
", 63302: "Insert"}; | 7518 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown
", 63302: "Insert"}; |
7343 CodeMirror.keyNames = keyNames; | 7519 CodeMirror.keyNames = keyNames; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7386 function lineEnd(cm, lineN) { | 7562 function lineEnd(cm, lineN) { |
7387 var merged, line = getLine(cm.doc, lineN); | 7563 var merged, line = getLine(cm.doc, lineN); |
7388 while (merged = collapsedSpanAtEnd(line)) { | 7564 while (merged = collapsedSpanAtEnd(line)) { |
7389 line = merged.find(1, true).line; | 7565 line = merged.find(1, true).line; |
7390 lineN = null; | 7566 lineN = null; |
7391 } | 7567 } |
7392 var order = getOrder(line); | 7568 var order = getOrder(line); |
7393 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l
ineRight(line); | 7569 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l
ineRight(line); |
7394 return Pos(lineN == null ? lineNo(line) : lineN, ch); | 7570 return Pos(lineN == null ? lineNo(line) : lineN, ch); |
7395 } | 7571 } |
| 7572 function lineStartSmart(cm, pos) { |
| 7573 var start = lineStart(cm, pos.line); |
| 7574 var line = getLine(cm.doc, start.line); |
| 7575 var order = getOrder(line); |
| 7576 if (!order || order[0].level == 0) { |
| 7577 var firstNonWS = Math.max(0, line.text.search(/\S/)); |
| 7578 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; |
| 7579 return Pos(start.line, inWS ? 0 : firstNonWS); |
| 7580 } |
| 7581 return start; |
| 7582 } |
7396 | 7583 |
7397 function compareBidiLevel(order, a, b) { | 7584 function compareBidiLevel(order, a, b) { |
7398 var linedir = order[0].level; | 7585 var linedir = order[0].level; |
7399 if (a == linedir) return true; | 7586 if (a == linedir) return true; |
7400 if (b == linedir) return false; | 7587 if (b == linedir) return false; |
7401 return a < b; | 7588 return a < b; |
7402 } | 7589 } |
7403 var bidiOther; | 7590 var bidiOther; |
7404 function getBidiPartAt(order, pos) { | 7591 function getBidiPartAt(order, pos) { |
7405 bidiOther = null; | 7592 bidiOther = null; |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7625 } | 7812 } |
7626 if (order[0].level != lst(order).level) | 7813 if (order[0].level != lst(order).level) |
7627 order.push(new BidiSpan(order[0].level, len, len)); | 7814 order.push(new BidiSpan(order[0].level, len, len)); |
7628 | 7815 |
7629 return order; | 7816 return order; |
7630 }; | 7817 }; |
7631 })(); | 7818 })(); |
7632 | 7819 |
7633 // THE END | 7820 // THE END |
7634 | 7821 |
7635 CodeMirror.version = "4.3.1"; | 7822 CodeMirror.version = "4.4.1"; |
7636 | 7823 |
7637 return CodeMirror; | 7824 return CodeMirror; |
7638 }); | 7825 }); |
OLD | NEW |