|
|
@ -1,7 +1,7 @@ |
|
|
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
|
|
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
|
|
|
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
|
|
|
|
|
|
|
// This is CodeMirror (http://codemirror.net), a code editor
|
|
|
|
// This is CodeMirror (https://codemirror.net), a code editor
|
|
|
|
// implemented in JavaScript on top of the browser's DOM.
|
|
|
|
//
|
|
|
|
// You can find some technical background for some of the code below
|
|
|
@ -746,6 +746,16 @@ function collapsedSpanAtSide(line, start) { |
|
|
|
function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } |
|
|
|
function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } |
|
|
|
|
|
|
|
function collapsedSpanAround(line, ch) { |
|
|
|
var sps = sawCollapsedSpans && line.markedSpans, found |
|
|
|
if (sps) { for (var i = 0; i < sps.length; ++i) { |
|
|
|
var sp = sps[i] |
|
|
|
if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && |
|
|
|
(!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker } |
|
|
|
} } |
|
|
|
return found |
|
|
|
} |
|
|
|
|
|
|
|
// Test whether there exists a collapsed span that partially
|
|
|
|
// overlaps (covers the start or end, but not both) of a new span.
|
|
|
|
// Such overlap is not allowed.
|
|
|
@ -1810,7 +1820,7 @@ function buildLineContent(cm, lineView) { |
|
|
|
var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, |
|
|
|
col: 0, pos: 0, cm: cm, |
|
|
|
trailingSpace: false, |
|
|
|
splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} |
|
|
|
splitSpaces: cm.getOption("lineWrapping")} |
|
|
|
lineView.measure = {} |
|
|
|
|
|
|
|
// Iterate over the logical lines that make up this visual line.
|
|
|
@ -1931,6 +1941,8 @@ function buildToken(builder, text, style, startStyle, endStyle, title, css) { |
|
|
|
builder.content.appendChild(content) |
|
|
|
} |
|
|
|
|
|
|
|
// Change some spaces to NBSP to prevent the browser from collapsing
|
|
|
|
// trailing spaces at the end of a line when rendering text (issue #1362).
|
|
|
|
function splitSpaces(text, trailingBefore) { |
|
|
|
if (text.length > 1 && !/ /.test(text)) { return text } |
|
|
|
var spaceBefore = trailingBefore, result = "" |
|
|
@ -2778,12 +2790,11 @@ function coordsChar(cm, x, y) { |
|
|
|
var lineObj = getLine(doc, lineN) |
|
|
|
for (;;) { |
|
|
|
var found = coordsCharInner(cm, lineObj, lineN, x, y) |
|
|
|
var merged = collapsedSpanAtEnd(lineObj) |
|
|
|
var mergedPos = merged && merged.find(0, true) |
|
|
|
if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) |
|
|
|
{ lineN = lineNo(lineObj = mergedPos.to.line) } |
|
|
|
else |
|
|
|
{ return found } |
|
|
|
var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0)) |
|
|
|
if (!collapsed) { return found } |
|
|
|
var rangeEnd = collapsed.find(1) |
|
|
|
if (rangeEnd.line == lineN) { return rangeEnd } |
|
|
|
lineObj = getLine(doc, lineN = rangeEnd.line) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -3543,6 +3554,7 @@ var NativeScrollbars = function(place, scroll, cm) { |
|
|
|
this.cm = cm |
|
|
|
var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") |
|
|
|
var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") |
|
|
|
vert.tabIndex = horiz.tabIndex = -1 |
|
|
|
place(vert); place(horiz) |
|
|
|
|
|
|
|
on(vert, "scroll", function () { |
|
|
@ -4783,7 +4795,7 @@ function addChangeToHistory(doc, change, selAfter, opId) { |
|
|
|
|
|
|
|
if ((hist.lastOp == opId || |
|
|
|
hist.lastOrigin == change.origin && change.origin && |
|
|
|
((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || |
|
|
|
((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || |
|
|
|
change.origin.charAt(0) == "*")) && |
|
|
|
(cur = lastChangeEvent(hist, hist.lastOp == opId))) { |
|
|
|
// Merge this change into the last event
|
|
|
@ -5684,7 +5696,7 @@ LineWidget.prototype.changed = function () { |
|
|
|
this.height = null |
|
|
|
var diff = widgetHeight(this) - oldH |
|
|
|
if (!diff) { return } |
|
|
|
updateLineHeight(line, line.height + diff) |
|
|
|
if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff) } |
|
|
|
if (cm) { |
|
|
|
runInOp(cm, function () { |
|
|
|
cm.curOp.forceUpdate = true |
|
|
@ -6567,8 +6579,6 @@ function registerGlobalHandlers() { |
|
|
|
// Called when the window resizes
|
|
|
|
function onResize(cm) { |
|
|
|
var d = cm.display |
|
|
|
if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) |
|
|
|
{ return } |
|
|
|
// Might be a text scaling operation, clear size caches.
|
|
|
|
d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null |
|
|
|
d.scrollbarsClipped = false |
|
|
@ -6614,7 +6624,7 @@ keyMap.pcDefault = { |
|
|
|
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", |
|
|
|
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore", |
|
|
|
"Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", |
|
|
|
fallthrough: "basic" |
|
|
|
"fallthrough": "basic" |
|
|
|
} |
|
|
|
// Very basic readline/emacs-style bindings, which are standard on Mac.
|
|
|
|
keyMap.emacsy = { |
|
|
@ -6632,7 +6642,7 @@ keyMap.macDefault = { |
|
|
|
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", |
|
|
|
"Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", |
|
|
|
"Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", |
|
|
|
fallthrough: ["basic", "emacsy"] |
|
|
|
"fallthrough": ["basic", "emacsy"] |
|
|
|
} |
|
|
|
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault |
|
|
|
|
|
|
@ -7234,8 +7244,9 @@ function onMouseDown(e) { |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
if (clickInGutter(cm, e)) { return } |
|
|
|
var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single" |
|
|
|
var button = e_button(e) |
|
|
|
if (button == 3 && captureRightClick ? contextMenuInGutter(cm, e) : clickInGutter(cm, e)) { return } |
|
|
|
var pos = posFromMouse(cm, e), repeat = pos ? clickRepeat(pos, button) : "single" |
|
|
|
window.focus() |
|
|
|
|
|
|
|
// #3261: make sure, that we're not starting a second selection
|
|
|
@ -7312,8 +7323,8 @@ function leftButtonStartDrag(cm, event, pos, behavior) { |
|
|
|
var dragEnd = operation(cm, function (e) { |
|
|
|
if (webkit) { display.scroller.draggable = false } |
|
|
|
cm.state.draggingText = false |
|
|
|
off(document, "mouseup", dragEnd) |
|
|
|
off(document, "mousemove", mouseMove) |
|
|
|
off(display.wrapper.ownerDocument, "mouseup", dragEnd) |
|
|
|
off(display.wrapper.ownerDocument, "mousemove", mouseMove) |
|
|
|
off(display.scroller, "dragstart", dragStart) |
|
|
|
off(display.scroller, "drop", dragEnd) |
|
|
|
if (!moved) { |
|
|
@ -7322,7 +7333,7 @@ function leftButtonStartDrag(cm, event, pos, behavior) { |
|
|
|
{ extendSelection(cm.doc, pos, null, null, behavior.extend) } |
|
|
|
// Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
|
|
|
|
if (webkit || ie && ie_version == 9) |
|
|
|
{ setTimeout(function () {document.body.focus(); display.input.focus()}, 20) } |
|
|
|
{ setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus()}, 20) } |
|
|
|
else |
|
|
|
{ display.input.focus() } |
|
|
|
} |
|
|
@ -7337,8 +7348,8 @@ function leftButtonStartDrag(cm, event, pos, behavior) { |
|
|
|
dragEnd.copy = !behavior.moveOnDrag |
|
|
|
// IE's approach to draggable
|
|
|
|
if (display.scroller.dragDrop) { display.scroller.dragDrop() } |
|
|
|
on(document, "mouseup", dragEnd) |
|
|
|
on(document, "mousemove", mouseMove) |
|
|
|
on(display.wrapper.ownerDocument, "mouseup", dragEnd) |
|
|
|
on(display.wrapper.ownerDocument, "mousemove", mouseMove) |
|
|
|
on(display.scroller, "dragstart", dragStart) |
|
|
|
on(display.scroller, "drop", dragEnd) |
|
|
|
|
|
|
@ -7470,19 +7481,19 @@ function leftButtonSelect(cm, event, start, behavior) { |
|
|
|
counter = Infinity |
|
|
|
e_preventDefault(e) |
|
|
|
display.input.focus() |
|
|
|
off(document, "mousemove", move) |
|
|
|
off(document, "mouseup", up) |
|
|
|
off(display.wrapper.ownerDocument, "mousemove", move) |
|
|
|
off(display.wrapper.ownerDocument, "mouseup", up) |
|
|
|
doc.history.lastSelOrigin = null |
|
|
|
} |
|
|
|
|
|
|
|
var move = operation(cm, function (e) { |
|
|
|
if (!e_button(e)) { done(e) } |
|
|
|
if (e.buttons === 0 || !e_button(e)) { done(e) } |
|
|
|
else { extend(e) } |
|
|
|
}) |
|
|
|
var up = operation(cm, done) |
|
|
|
cm.state.selectingText = up |
|
|
|
on(document, "mousemove", move) |
|
|
|
on(document, "mouseup", up) |
|
|
|
on(display.wrapper.ownerDocument, "mousemove", move) |
|
|
|
on(display.wrapper.ownerDocument, "mouseup", up) |
|
|
|
} |
|
|
|
|
|
|
|
// Used when mouse-selecting to adjust the anchor to the proper side
|
|
|
@ -7714,6 +7725,7 @@ function defineOptions(CodeMirror) { |
|
|
|
option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }) |
|
|
|
option("autofocus", null) |
|
|
|
option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true) |
|
|
|
option("phrases", null) |
|
|
|
} |
|
|
|
|
|
|
|
function guttersChanged(cm) { |
|
|
@ -7765,6 +7777,7 @@ function CodeMirror(place, options) { |
|
|
|
|
|
|
|
var doc = options.value |
|
|
|
if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) } |
|
|
|
else if (options.mode) { doc.modeOption = options.mode } |
|
|
|
this.doc = doc |
|
|
|
|
|
|
|
var input = new CodeMirror.inputStyles[options.inputStyle](this) |
|
|
@ -8551,6 +8564,11 @@ function addEditorMethods(CodeMirror) { |
|
|
|
return old |
|
|
|
}), |
|
|
|
|
|
|
|
phrase: function(phraseText) { |
|
|
|
var phrases = this.options.phrases |
|
|
|
return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText |
|
|
|
}, |
|
|
|
|
|
|
|
getInputField: function(){return this.display.input.getField()}, |
|
|
|
getWrapperElement: function(){return this.display.wrapper}, |
|
|
|
getScrollerElement: function(){return this.display.scroller}, |
|
|
@ -8755,8 +8773,12 @@ ContentEditableInput.prototype.showSelection = function (info, takeFocus) { |
|
|
|
this.showMultipleSelections(info) |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.getSelection = function () { |
|
|
|
return this.cm.display.wrapper.ownerDocument.getSelection() |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.showPrimarySelection = function () { |
|
|
|
var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary() |
|
|
|
var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary() |
|
|
|
var from = prim.from(), to = prim.to() |
|
|
|
|
|
|
|
if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { |
|
|
@ -8823,13 +8845,13 @@ ContentEditableInput.prototype.showMultipleSelections = function (info) { |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.rememberSelection = function () { |
|
|
|
var sel = window.getSelection() |
|
|
|
var sel = this.getSelection() |
|
|
|
this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset |
|
|
|
this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.selectionInEditor = function () { |
|
|
|
var sel = window.getSelection() |
|
|
|
var sel = this.getSelection() |
|
|
|
if (!sel.rangeCount) { return false } |
|
|
|
var node = sel.getRangeAt(0).commonAncestorContainer |
|
|
|
return contains(this.div, node) |
|
|
@ -8864,14 +8886,14 @@ ContentEditableInput.prototype.receivedFocus = function () { |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.selectionChanged = function () { |
|
|
|
var sel = window.getSelection() |
|
|
|
var sel = this.getSelection() |
|
|
|
return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || |
|
|
|
sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.pollSelection = function () { |
|
|
|
if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } |
|
|
|
var sel = window.getSelection(), cm = this.cm |
|
|
|
var sel = this.getSelection(), cm = this.cm |
|
|
|
// On Android Chrome (version 56, at least), backspacing into an
|
|
|
|
// uneditable block element will put the cursor in that element,
|
|
|
|
// and then, because it's not editable, hide the virtual keyboard.
|
|
|
@ -9005,7 +9027,7 @@ ContentEditableInput.prototype.setUneditable = function (node) { |
|
|
|
}; |
|
|
|
|
|
|
|
ContentEditableInput.prototype.onKeyPress = function (e) { |
|
|
|
if (e.charCode == 0) { return } |
|
|
|
if (e.charCode == 0 || this.composing) { return } |
|
|
|
e.preventDefault() |
|
|
|
if (!this.cm.isReadOnly()) |
|
|
|
{ operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) } |
|
|
@ -9045,12 +9067,13 @@ function isInGutter(node) { |
|
|
|
function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } |
|
|
|
|
|
|
|
function domTextBetween(cm, from, to, fromLine, toLine) { |
|
|
|
var text = "", closing = false, lineSep = cm.doc.lineSeparator() |
|
|
|
var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false |
|
|
|
function recognizeMarker(id) { return function (marker) { return marker.id == id; } } |
|
|
|
function close() { |
|
|
|
if (closing) { |
|
|
|
text += lineSep |
|
|
|
closing = false |
|
|
|
if (extraLinebreak) { text += lineSep } |
|
|
|
closing = extraLinebreak = false |
|
|
|
} |
|
|
|
} |
|
|
|
function addText(str) { |
|
|
@ -9062,8 +9085,8 @@ function domTextBetween(cm, from, to, fromLine, toLine) { |
|
|
|
function walk(node) { |
|
|
|
if (node.nodeType == 1) { |
|
|
|
var cmText = node.getAttribute("cm-text") |
|
|
|
if (cmText != null) { |
|
|
|
addText(cmText || node.textContent.replace(/\u200b/g, "")) |
|
|
|
if (cmText) { |
|
|
|
addText(cmText) |
|
|
|
return |
|
|
|
} |
|
|
|
var markerID = node.getAttribute("cm-marker"), range |
|
|
@ -9074,19 +9097,24 @@ function domTextBetween(cm, from, to, fromLine, toLine) { |
|
|
|
return |
|
|
|
} |
|
|
|
if (node.getAttribute("contenteditable") == "false") { return } |
|
|
|
var isBlock = /^(pre|div|p)$/i.test(node.nodeName) |
|
|
|
var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName) |
|
|
|
if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } |
|
|
|
|
|
|
|
if (isBlock) { close() } |
|
|
|
for (var i = 0; i < node.childNodes.length; i++) |
|
|
|
{ walk(node.childNodes[i]) } |
|
|
|
|
|
|
|
if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true } |
|
|
|
if (isBlock) { closing = true } |
|
|
|
} else if (node.nodeType == 3) { |
|
|
|
addText(node.nodeValue) |
|
|
|
addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")) |
|
|
|
} |
|
|
|
} |
|
|
|
for (;;) { |
|
|
|
walk(from) |
|
|
|
if (from == to) { break } |
|
|
|
from = from.nextSibling |
|
|
|
extraLinebreak = false |
|
|
|
} |
|
|
|
return text |
|
|
|
} |
|
|
@ -9187,13 +9215,10 @@ TextareaInput.prototype.init = function (display) { |
|
|
|
var this$1 = this; |
|
|
|
|
|
|
|
var input = this, cm = this.cm |
|
|
|
this.createField(display) |
|
|
|
var te = this.textarea |
|
|
|
|
|
|
|
// Wraps and hides input textarea
|
|
|
|
var div = this.wrapper = hiddenTextarea() |
|
|
|
// The semihidden textarea that is focused when the editor is
|
|
|
|
// focused, and receives input.
|
|
|
|
var te = this.textarea = div.firstChild |
|
|
|
display.wrapper.insertBefore(div, display.wrapper.firstChild) |
|
|
|
display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild) |
|
|
|
|
|
|
|
// Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
|
|
|
|
if (ios) { te.style.width = "0px" } |
|
|
@ -9260,6 +9285,14 @@ TextareaInput.prototype.init = function (display) { |
|
|
|
}) |
|
|
|
}; |
|
|
|
|
|
|
|
TextareaInput.prototype.createField = function (_display) { |
|
|
|
// Wraps and hides input textarea
|
|
|
|
this.wrapper = hiddenTextarea() |
|
|
|
// The semihidden textarea that is focused when the editor is
|
|
|
|
// focused, and receives input.
|
|
|
|
this.textarea = this.wrapper.firstChild |
|
|
|
}; |
|
|
|
|
|
|
|
TextareaInput.prototype.prepareSelection = function () { |
|
|
|
// Redraw the selection and/or cursor
|
|
|
|
var cm = this.cm, display = cm.display, doc = cm.doc |
|
|
@ -9653,7 +9686,7 @@ CodeMirror.fromTextArea = fromTextArea |
|
|
|
|
|
|
|
addLegacyProps(CodeMirror) |
|
|
|
|
|
|
|
CodeMirror.version = "5.35.0" |
|
|
|
CodeMirror.version = "5.40.2" |
|
|
|
|
|
|
|
return CodeMirror; |
|
|
|
|