From 1a234722268e57a75a2f3278a1e2d67b401e3104 Mon Sep 17 00:00:00 2001 From: Repellent Date: Sun, 12 Apr 2026 21:18:46 +0500 Subject: [PATCH] update CodeMirror v 5.65.21 --- .../codemirror/addon/comment/comment.js | 8 +- .../addon/comment/continuecomment.js | 2 +- .../codemirror/addon/dialog/dialog.js | 6 +- .../codemirror/addon/display/autorefresh.js | 2 +- .../codemirror/addon/display/fullscreen.js | 2 +- .../codemirror/addon/display/panel.js | 12 +- .../codemirror/addon/display/placeholder.js | 17 +- .../codemirror/addon/display/rulers.js | 2 +- .../codemirror/addon/edit/closebrackets.js | 24 +- .../codemirror/addon/edit/closetag.js | 11 +- .../codemirror/addon/edit/continuelist.js | 10 +- .../codemirror/addon/edit/matchbrackets.js | 32 +- .../codemirror/addon/edit/matchtags.js | 2 +- .../codemirror/addon/edit/trailingspace.js | 2 +- .../codemirror/addon/fold/brace-fold.js | 96 +- .../codemirror/addon/fold/comment-fold.js | 2 +- .../codemirror/addon/fold/foldcode.js | 12 +- .../codemirror/addon/fold/foldgutter.js | 8 +- .../codemirror/addon/fold/indent-fold.js | 2 +- .../codemirror/addon/fold/markdown-fold.js | 2 +- .../codemirror/addon/fold/xml-fold.js | 2 +- .../codemirror/addon/hint/anyword-hint.js | 2 +- .../codemirror/addon/hint/css-hint.js | 2 +- .../codemirror/addon/hint/html-hint.js | 3 +- .../codemirror/addon/hint/javascript-hint.js | 9 +- .../codemirror/addon/hint/show-hint.css | 1 + .../codemirror/addon/hint/show-hint.js | 131 ++- .../codemirror/addon/hint/sql-hint.js | 42 +- .../codemirror/addon/hint/xml-hint.js | 25 +- .../addon/lint/coffeescript-lint.js | 2 +- .../codemirror/addon/lint/css-lint.js | 2 +- .../codemirror/addon/lint/html-lint.js | 2 +- .../codemirror/addon/lint/javascript-lint.js | 4 +- .../codemirror/addon/lint/json-lint.js | 2 +- lib/redactor/codemirror/addon/lint/lint.css | 32 +- lib/redactor/codemirror/addon/lint/lint.js | 114 ++- .../codemirror/addon/lint/yaml-lint.js | 2 +- lib/redactor/codemirror/addon/merge/merge.js | 34 +- .../codemirror/addon/mode/loadmode.js | 24 +- .../codemirror/addon/mode/multiplex.js | 15 +- .../codemirror/addon/mode/multiplex_test.js | 18 +- lib/redactor/codemirror/addon/mode/overlay.js | 2 +- lib/redactor/codemirror/addon/mode/simple.js | 6 +- .../codemirror/addon/runmode/colorize.js | 4 +- .../addon/runmode/runmode-standalone.js | 442 ++++++--- .../codemirror/addon/runmode/runmode.js | 20 +- .../codemirror/addon/runmode/runmode.node.js | 398 +++++--- .../addon/scroll/annotatescrollbar.js | 12 +- .../codemirror/addon/scroll/scrollpastend.js | 2 +- .../addon/scroll/simplescrollbars.js | 2 +- .../codemirror/addon/search/jump-to-line.js | 7 +- .../addon/search/match-highlighter.js | 8 +- .../addon/search/matchesonscrollbar.js | 2 +- .../codemirror/addon/search/search.js | 53 +- .../codemirror/addon/search/searchcursor.js | 33 +- .../codemirror/addon/selection/active-line.js | 2 +- .../addon/selection/mark-selection.js | 2 +- .../addon/selection/selection-pointer.js | 2 +- lib/redactor/codemirror/addon/tern/tern.js | 60 +- lib/redactor/codemirror/addon/tern/worker.js | 2 +- .../codemirror/addon/wrap/hardwrap.js | 33 +- lib/redactor/codemirror/lib/codemirror.css | 34 +- lib/redactor/codemirror/lib/codemirror.js | 890 ++++++++++-------- lib/redactor/codemirror/mode/clike/clike.js | 43 +- lib/redactor/codemirror/mode/clike/index.html | 380 ++++++++ lib/redactor/codemirror/mode/clike/scala.html | 767 +++++++++++++++ lib/redactor/codemirror/mode/clike/test.js | 170 ++++ lib/redactor/codemirror/mode/css/css.js | 281 +++--- lib/redactor/codemirror/mode/css/gss.html | 104 ++ lib/redactor/codemirror/mode/css/gss_test.js | 17 + lib/redactor/codemirror/mode/css/index.html | 81 ++ lib/redactor/codemirror/mode/css/less.html | 152 +++ lib/redactor/codemirror/mode/css/less_test.js | 54 ++ lib/redactor/codemirror/mode/css/scss.html | 158 ++++ lib/redactor/codemirror/mode/css/scss_test.js | 110 +++ lib/redactor/codemirror/mode/css/test.js | 217 +++++ .../mode/htmlembedded/htmlembedded.js | 2 +- .../codemirror/mode/htmlembedded/index.html | 60 ++ .../codemirror/mode/htmlmixed/htmlmixed.js | 7 +- .../codemirror/mode/htmlmixed/index.html | 100 ++ .../codemirror/mode/javascript/index.html | 118 +++ .../codemirror/mode/javascript/javascript.js | 82 +- .../codemirror/mode/javascript/json-ld.html | 72 ++ .../codemirror/mode/javascript/test.js | 521 ++++++++++ .../mode/javascript/typescript.html | 62 ++ lib/redactor/codemirror/mode/meta.js | 17 +- lib/redactor/codemirror/mode/php/index.html | 64 ++ lib/redactor/codemirror/mode/php/php.js | 14 +- lib/redactor/codemirror/mode/php/test.js | 154 +++ .../codemirror/mode/smarty/index.html | 138 +++ lib/redactor/codemirror/mode/smarty/smarty.js | 2 +- lib/redactor/codemirror/mode/sql/index.html | 92 ++ lib/redactor/codemirror/mode/sql/sql.js | 102 +- lib/redactor/codemirror/theme/abbott.css | 268 ++++++ lib/redactor/codemirror/theme/ayu-dark.css | 2 + lib/redactor/codemirror/theme/ayu-mirage.css | 4 +- lib/redactor/codemirror/theme/base16-dark.css | 2 + lib/redactor/codemirror/theme/bespin.css | 2 +- .../codemirror/theme/duotone-dark.css | 2 +- .../codemirror/theme/duotone-light.css | 2 +- .../codemirror/theme/gruvbox-dark.css | 2 + lib/redactor/codemirror/theme/juejin.css | 30 + .../codemirror/theme/material-ocean.css | 8 +- .../codemirror/theme/material-palenight.css | 8 +- lib/redactor/codemirror/theme/material.css | 8 +- .../codemirror/theme/oceanic-next.css | 2 + lib/redactor/codemirror/theme/solarized.css | 5 +- lib/redactor/codemirror/theme/zenburn.css | 2 +- 108 files changed, 5975 insertions(+), 1251 deletions(-) create mode 100644 lib/redactor/codemirror/mode/clike/index.html create mode 100644 lib/redactor/codemirror/mode/clike/scala.html create mode 100644 lib/redactor/codemirror/mode/clike/test.js create mode 100644 lib/redactor/codemirror/mode/css/gss.html create mode 100644 lib/redactor/codemirror/mode/css/gss_test.js create mode 100644 lib/redactor/codemirror/mode/css/index.html create mode 100644 lib/redactor/codemirror/mode/css/less.html create mode 100644 lib/redactor/codemirror/mode/css/less_test.js create mode 100644 lib/redactor/codemirror/mode/css/scss.html create mode 100644 lib/redactor/codemirror/mode/css/scss_test.js create mode 100644 lib/redactor/codemirror/mode/css/test.js create mode 100644 lib/redactor/codemirror/mode/htmlembedded/index.html create mode 100644 lib/redactor/codemirror/mode/htmlmixed/index.html create mode 100644 lib/redactor/codemirror/mode/javascript/index.html create mode 100644 lib/redactor/codemirror/mode/javascript/json-ld.html create mode 100644 lib/redactor/codemirror/mode/javascript/test.js create mode 100644 lib/redactor/codemirror/mode/javascript/typescript.html create mode 100644 lib/redactor/codemirror/mode/php/index.html create mode 100644 lib/redactor/codemirror/mode/php/test.js create mode 100644 lib/redactor/codemirror/mode/smarty/index.html create mode 100644 lib/redactor/codemirror/mode/sql/index.html create mode 100644 lib/redactor/codemirror/theme/abbott.css create mode 100644 lib/redactor/codemirror/theme/juejin.css diff --git a/lib/redactor/codemirror/addon/comment/comment.js b/lib/redactor/codemirror/addon/comment/comment.js index 8394e85..7f6f3ed 100644 --- a/lib/redactor/codemirror/addon/comment/comment.js +++ b/lib/redactor/codemirror/addon/comment/comment.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -13,7 +13,7 @@ var noOptions = {}; var nonWS = /[^\s\u00a0]/; - var Pos = CodeMirror.Pos; + var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos; function firstNonWS(str) { var found = str.search(nonWS); @@ -78,7 +78,7 @@ var baseString = null; for (var i = from.line; i < end; ++i) { var line = self.getLine(i); - var whitespace = line.slice(0, firstNonWS(line)); + var whitespace = line.search(nonWS) === -1 ? line : line.slice(0, firstNonWS(line)); if (baseString == null || baseString.length > whitespace.length) { baseString = whitespace; } @@ -126,7 +126,9 @@ if (i != end || lastLineHasText) self.replaceRange(lead + pad, Pos(i, 0)); } else { + var atCursor = cmp(self.getCursor("to"), to) == 0, empty = !self.somethingSelected() self.replaceRange(endString, to); + if (atCursor) self.setSelection(empty ? to : self.getCursor("from"), to) self.replaceRange(startString, from); } }); diff --git a/lib/redactor/codemirror/addon/comment/continuecomment.js b/lib/redactor/codemirror/addon/comment/continuecomment.js index 7ca1b4a..2f16da8 100644 --- a/lib/redactor/codemirror/addon/comment/continuecomment.js +++ b/lib/redactor/codemirror/addon/comment/continuecomment.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/dialog/dialog.js b/lib/redactor/codemirror/addon/dialog/dialog.js index 23b06a8..0294d23 100644 --- a/lib/redactor/codemirror/addon/dialog/dialog.js +++ b/lib/redactor/codemirror/addon/dialog/dialog.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Open simple dialogs on top of an editor. Relies on dialog.css. @@ -82,7 +82,9 @@ if (e.keyCode == 13) callback(inp.value, e); }); - if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close); + if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) { + if (evt.relatedTarget !== null) close(); + }); } else if (button = dialog.getElementsByTagName("button")[0]) { CodeMirror.on(button, "click", function() { close(); diff --git a/lib/redactor/codemirror/addon/display/autorefresh.js b/lib/redactor/codemirror/addon/display/autorefresh.js index 37014dc..b5e6ab0 100644 --- a/lib/redactor/codemirror/addon/display/autorefresh.js +++ b/lib/redactor/codemirror/addon/display/autorefresh.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/display/fullscreen.js b/lib/redactor/codemirror/addon/display/fullscreen.js index eda7300..39451bd 100644 --- a/lib/redactor/codemirror/addon/display/fullscreen.js +++ b/lib/redactor/codemirror/addon/display/fullscreen.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/display/panel.js b/lib/redactor/codemirror/addon/display/panel.js index 4c9f2c0..b00d683 100644 --- a/lib/redactor/codemirror/addon/display/panel.js +++ b/lib/redactor/codemirror/addon/display/panel.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function (mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -76,7 +76,7 @@ }; function initPanels(cm) { - var wrap = cm.getWrapperElement(); + var wrap = cm.getWrapperElement() var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle; var height = parseInt(style.height); var info = cm.state.panels = { @@ -84,9 +84,10 @@ panels: [], wrapper: document.createElement("div") }; + var hasFocus = cm.hasFocus(), scrollPos = cm.getScrollInfo() wrap.parentNode.insertBefore(info.wrapper, wrap); - var hasFocus = cm.hasFocus(); info.wrapper.appendChild(wrap); + cm.scrollTo(scrollPos.left, scrollPos.top) if (hasFocus) cm.focus(); cm._setSize = cm.setSize; @@ -114,8 +115,11 @@ var info = cm.state.panels; cm.state.panels = null; - var wrap = cm.getWrapperElement(); + var wrap = cm.getWrapperElement() + var hasFocus = cm.hasFocus(), scrollPos = cm.getScrollInfo() info.wrapper.parentNode.replaceChild(wrap, info.wrapper); + cm.scrollTo(scrollPos.left, scrollPos.top) + if (hasFocus) cm.focus(); wrap.style.height = info.setHeight; cm.setSize = cm._setSize; cm.setSize(); diff --git a/lib/redactor/codemirror/addon/display/placeholder.js b/lib/redactor/codemirror/addon/display/placeholder.js index 4eabe3d..7848f51 100644 --- a/lib/redactor/codemirror/addon/display/placeholder.js +++ b/lib/redactor/codemirror/addon/display/placeholder.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -15,11 +15,13 @@ cm.on("blur", onBlur); cm.on("change", onChange); cm.on("swapDoc", onChange); + CodeMirror.on(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose = function() { onComposition(cm) }) onChange(cm); } else if (!val && prev) { cm.off("blur", onBlur); cm.off("change", onChange); cm.off("swapDoc", onChange); + CodeMirror.off(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose) clearPlaceholder(cm); var wrapper = cm.getWrapperElement(); wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); @@ -46,6 +48,19 @@ cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); } + function onComposition(cm) { + setTimeout(function() { + var empty = false + if (cm.lineCount() == 1) { + var input = cm.getInputField() + empty = input.nodeName == "TEXTAREA" ? !cm.getLine(0).length + : !/[^\u200b]/.test(input.querySelector(".CodeMirror-line").textContent) + } + if (empty) setPlaceholder(cm) + else clearPlaceholder(cm) + }, 20) + } + function onBlur(cm) { if (isEmpty(cm)) setPlaceholder(cm); } diff --git a/lib/redactor/codemirror/addon/display/rulers.js b/lib/redactor/codemirror/addon/display/rulers.js index 0bb83bb..4ca15ea 100644 --- a/lib/redactor/codemirror/addon/display/rulers.js +++ b/lib/redactor/codemirror/addon/display/rulers.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/edit/closebrackets.js b/lib/redactor/codemirror/addon/edit/closebrackets.js index 4415c39..badbbd8 100644 --- a/lib/redactor/codemirror/addon/edit/closebrackets.js +++ b/lib/redactor/codemirror/addon/edit/closebrackets.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -87,7 +87,7 @@ cm.operation(function() { var linesep = cm.lineSeparator() || "\n"; cm.replaceSelection(linesep + linesep, null); - cm.execCommand("goCharLeft"); + moveSel(cm, -1) ranges = cm.listSelections(); for (var i = 0; i < ranges.length; i++) { var line = ranges[i].head.line; @@ -97,6 +97,17 @@ }); } + function moveSel(cm, dir) { + var newRanges = [], ranges = cm.listSelections(), primary = 0 + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i] + if (range.head == cm.getCursor()) primary = i + var pos = range.head.ch || dir > 0 ? {line: range.head.line, ch: range.head.ch + dir} : {line: range.head.line - 1} + newRanges.push({anchor: pos, head: pos}) + } + cm.setSelections(newRanges, primary) + } + function contractSelection(sel) { var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0; return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)), @@ -153,10 +164,9 @@ var right = pos % 2 ? ch : pairs.charAt(pos + 1); cm.operation(function() { if (type == "skip") { - cm.execCommand("goCharRight"); + moveSel(cm, 1) } else if (type == "skipThree") { - for (var i = 0; i < 3; i++) - cm.execCommand("goCharRight"); + moveSel(cm, 3) } else if (type == "surround") { var sels = cm.getSelections(); for (var i = 0; i < sels.length; i++) @@ -169,10 +179,10 @@ } else if (type == "both") { cm.replaceSelection(left + right, null); cm.triggerElectric(left + right); - cm.execCommand("goCharLeft"); + moveSel(cm, -1) } else if (type == "addFour") { cm.replaceSelection(left + left + left + left, "before"); - cm.execCommand("goCharRight"); + moveSel(cm, 1) } }); } diff --git a/lib/redactor/codemirror/addon/edit/closetag.js b/lib/redactor/codemirror/addon/edit/closetag.js index b8cbf95..62c0578 100644 --- a/lib/redactor/codemirror/addon/edit/closetag.js +++ b/lib/redactor/codemirror/addon/edit/closetag.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE /** * Tag-closer extension for CodeMirror. @@ -40,9 +40,9 @@ cm.removeKeyMap("autoCloseTags"); if (!val) return; var map = {name: "autoCloseTags"}; - if (typeof val != "object" || val.whenClosing) + if (typeof val != "object" || val.whenClosing !== false) map["'/'"] = function(cm) { return autoCloseSlash(cm); }; - if (typeof val != "object" || val.whenOpening) + if (typeof val != "object" || val.whenOpening !== false) map["'>'"] = function(cm) { return autoCloseGT(cm); }; cm.addKeyMap(map); }); @@ -128,9 +128,10 @@ replacement = head + "style"; } else { var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) - if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos))) + var top = context.length ? context[context.length - 1] : "" + if (!context || (context.length && closingTagExists(cm, context, top, pos))) return CodeMirror.Pass; - replacement = head + context[context.length - 1] + replacement = head + top } if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">"; replacements[i] = replacement; diff --git a/lib/redactor/codemirror/addon/edit/continuelist.js b/lib/redactor/codemirror/addon/edit/continuelist.js index fb5f037..915d813 100644 --- a/lib/redactor/codemirror/addon/edit/continuelist.js +++ b/lib/redactor/codemirror/addon/edit/continuelist.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -24,7 +24,7 @@ // If we're not in Markdown mode, fall back to normal newlineAndIndent var eolState = cm.getStateAfter(pos.line); var inner = CodeMirror.innerMode(cm.getMode(), eolState); - if (inner.mode.name !== "markdown") { + if (inner.mode.name !== "markdown" && inner.mode.helperType !== "markdown") { cm.execCommand("newlineAndIndent"); return; } else { @@ -41,7 +41,9 @@ return; } if (emptyListRE.test(line)) { - if (!/>\s*$/.test(line)) cm.replaceRange("", { + var endOfQuote = inQuote && />\s*$/.test(line) + var endOfList = !/>\s*$/.test(line) + if (endOfQuote || endOfList) cm.replaceRange("", { line: pos.line, ch: 0 }, { line: pos.line, ch: pos.ch + 1 @@ -88,7 +90,7 @@ }); } else { if (startIndent.length > nextIndent.length) return; - // This doesn't run if the next line immediatley indents, as it is + // This doesn't run if the next line immediately indents, as it is // not clear of the users intention (new indented item or same level) if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return; skipCount += 1; diff --git a/lib/redactor/codemirror/addon/edit/matchbrackets.js b/lib/redactor/codemirror/addon/edit/matchbrackets.js index 2a14728..0d1bcb6 100644 --- a/lib/redactor/codemirror/addon/edit/matchbrackets.js +++ b/lib/redactor/codemirror/addon/edit/matchbrackets.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -27,7 +27,7 @@ afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) var re = bracketRegex(config) - // A cursor is defined as between two characters, but in in vim command mode + // A cursor is defined as between two characters, but in vim command mode // (i.e. not insert mode), the cursor is visually represented as a // highlighted box on top of the 2nd character. Otherwise, we allow matches // from before or after the cursor. @@ -38,7 +38,7 @@ if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); - var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); if (found == null) return null; return {from: Pos(where.line, pos), to: found && found.pos, match: found && found.ch == match.charAt(0), forward: dir > 0}; @@ -67,7 +67,8 @@ if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); for (; pos != end; pos += dir) { var ch = line.charAt(pos); - if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { var match = matching[ch]; if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; @@ -80,11 +81,12 @@ function matchBrackets(cm, autoclear, config) { // Disable brace matching in long lines, since it'll cause hugely slow updates - var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; var marks = [], ranges = cm.listSelections(); for (var i = 0; i < ranges.length; i++) { var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); - if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) @@ -94,7 +96,7 @@ if (marks.length) { // Kludge to work around the IE bug from issue #1193, where text - // input stops going to the textare whever this fires. + // input stops going to the textarea whenever this fires. if (ie_lt8 && cm.state.focused) cm.focus(); var clear = function() { @@ -117,17 +119,25 @@ }); } + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { if (old && old != CodeMirror.Init) { cm.off("cursorActivity", doMatchBrackets); - if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { - cm.state.matchBrackets.currentlyHighlighted(); - cm.state.matchBrackets.currentlyHighlighted = null; - } + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); } if (val) { cm.state.matchBrackets = typeof val == "object" ? val : {}; cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) } }); diff --git a/lib/redactor/codemirror/addon/edit/matchtags.js b/lib/redactor/codemirror/addon/edit/matchtags.js index 2203d93..afa6105 100644 --- a/lib/redactor/codemirror/addon/edit/matchtags.js +++ b/lib/redactor/codemirror/addon/edit/matchtags.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/edit/trailingspace.js b/lib/redactor/codemirror/addon/edit/trailingspace.js index c39c310..8d01c20 100644 --- a/lib/redactor/codemirror/addon/edit/trailingspace.js +++ b/lib/redactor/codemirror/addon/edit/trailingspace.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/fold/brace-fold.js b/lib/redactor/codemirror/addon/fold/brace-fold.js index 654d1fb..2124768 100644 --- a/lib/redactor/codemirror/addon/fold/brace-fold.js +++ b/lib/redactor/codemirror/addon/fold/brace-fold.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -11,53 +11,67 @@ })(function(CodeMirror) { "use strict"; -CodeMirror.registerHelper("fold", "brace", function(cm, start) { - var line = start.line, lineText = cm.getLine(line); - var tokenType; +function bracketFolding(pairs) { + return function(cm, start) { + var line = start.line, lineText = cm.getLine(line); - function findOpening(openCh) { - for (var at = start.ch, pass = 0;;) { - var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); - if (found == -1) { - if (pass == 1) break; - pass = 1; - at = lineText.length; - continue; + function findOpening(pair) { + var tokenType; + for (var at = start.ch, pass = 0;;) { + var found = at <= 0 ? -1 : lineText.lastIndexOf(pair[0], at - 1); + if (found == -1) { + if (pass == 1) break; + pass = 1; + at = lineText.length; + continue; + } + if (pass == 1 && found < start.ch) break; + tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); + if (!/^(comment|string)/.test(tokenType)) return {ch: found + 1, tokenType: tokenType, pair: pair}; + at = found - 1; } - if (pass == 1 && found < start.ch) break; - tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); - if (!/^(comment|string)/.test(tokenType)) return found + 1; - at = found - 1; } - } - var startToken = "{", endToken = "}", startCh = findOpening("{"); - if (startCh == null) { - startToken = "[", endToken = "]"; - startCh = findOpening("["); - } - - if (startCh == null) return; - var count = 1, lastLine = cm.lastLine(), end, endCh; - outer: for (var i = line; i <= lastLine; ++i) { - var text = cm.getLine(i), pos = i == line ? startCh : 0; - for (;;) { - var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); - if (nextOpen < 0) nextOpen = text.length; - if (nextClose < 0) nextClose = text.length; - pos = Math.min(nextOpen, nextClose); - if (pos == text.length) break; - if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { - if (pos == nextOpen) ++count; - else if (!--count) { end = i; endCh = pos; break outer; } + function findRange(found) { + var count = 1, lastLine = cm.lastLine(), end, startCh = found.ch, endCh + outer: for (var i = line; i <= lastLine; ++i) { + var text = cm.getLine(i), pos = i == line ? startCh : 0; + for (;;) { + var nextOpen = text.indexOf(found.pair[0], pos), nextClose = text.indexOf(found.pair[1], pos); + if (nextOpen < 0) nextOpen = text.length; + if (nextClose < 0) nextClose = text.length; + pos = Math.min(nextOpen, nextClose); + if (pos == text.length) break; + if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == found.tokenType) { + if (pos == nextOpen) ++count; + else if (!--count) { end = i; endCh = pos; break outer; } + } + ++pos; + } } - ++pos; + + if (end == null || line == end) return null + return {from: CodeMirror.Pos(line, startCh), + to: CodeMirror.Pos(end, endCh)}; } + + var found = [] + for (var i = 0; i < pairs.length; i++) { + var open = findOpening(pairs[i]) + if (open) found.push(open) + } + found.sort(function(a, b) { return a.ch - b.ch }) + for (var i = 0; i < found.length; i++) { + var range = findRange(found[i]) + if (range) return range + } + return null } - if (end == null || line == end) return; - return {from: CodeMirror.Pos(line, startCh), - to: CodeMirror.Pos(end, endCh)}; -}); +} + +CodeMirror.registerHelper("fold", "brace", bracketFolding([["{", "}"], ["[", "]"]])); + +CodeMirror.registerHelper("fold", "brace-paren", bracketFolding([["{", "}"], ["[", "]"], ["(", ")"]])); CodeMirror.registerHelper("fold", "import", function(cm, start) { function hasImport(line) { diff --git a/lib/redactor/codemirror/addon/fold/comment-fold.js b/lib/redactor/codemirror/addon/fold/comment-fold.js index 836101d..adaa79d 100644 --- a/lib/redactor/codemirror/addon/fold/comment-fold.js +++ b/lib/redactor/codemirror/addon/fold/comment-fold.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/fold/foldcode.js b/lib/redactor/codemirror/addon/fold/foldcode.js index 887df3f..a6a51af 100644 --- a/lib/redactor/codemirror/addon/fold/foldcode.js +++ b/lib/redactor/codemirror/addon/fold/foldcode.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -24,9 +24,11 @@ function getRange(allowFolded) { var range = finder(cm, pos); if (!range || range.to.line - range.from.line < minSize) return null; + if (force === "fold") return range; + var marks = cm.findMarksAt(range.from); for (var i = 0; i < marks.length; ++i) { - if (marks[i].__isFold && force !== "fold") { + if (marks[i].__isFold) { if (!allowFolded) return null; range.cleared = true; marks[i].clear(); @@ -99,18 +101,18 @@ cm.foldCode(cm.getCursor(), null, "fold"); }; CodeMirror.commands.unfold = function(cm) { - cm.foldCode(cm.getCursor(), null, "unfold"); + cm.foldCode(cm.getCursor(), { scanUp: false }, "unfold"); }; CodeMirror.commands.foldAll = function(cm) { cm.operation(function() { for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) - cm.foldCode(CodeMirror.Pos(i, 0), null, "fold"); + cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, "fold"); }); }; CodeMirror.commands.unfoldAll = function(cm) { cm.operation(function() { for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) - cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold"); + cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, "unfold"); }); }; diff --git a/lib/redactor/codemirror/addon/fold/foldgutter.js b/lib/redactor/codemirror/addon/fold/foldgutter.js index 7d46a60..8fc0e99 100644 --- a/lib/redactor/codemirror/addon/fold/foldgutter.js +++ b/lib/redactor/codemirror/addon/fold/foldgutter.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -21,6 +21,7 @@ cm.off("fold", onFold); cm.off("unfold", onFold); cm.off("swapDoc", onChange); + cm.off("optionChange", optionChange); } if (val) { cm.state.foldGutter = new State(parseOptions(val)); @@ -31,6 +32,7 @@ cm.on("fold", onFold); cm.on("unfold", onFold); cm.on("swapDoc", onChange); + cm.on("optionChange", optionChange); } }); @@ -120,6 +122,10 @@ else cm.foldCode(Pos(line, 0), opts); } + function optionChange(cm, option) { + if (option == "mode") onChange(cm) + } + function onChange(cm) { var state = cm.state.foldGutter; if (!state) return; diff --git a/lib/redactor/codemirror/addon/fold/indent-fold.js b/lib/redactor/codemirror/addon/fold/indent-fold.js index 0cc1126..470e84a 100644 --- a/lib/redactor/codemirror/addon/fold/indent-fold.js +++ b/lib/redactor/codemirror/addon/fold/indent-fold.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/fold/markdown-fold.js b/lib/redactor/codemirror/addon/fold/markdown-fold.js index 6a55178..4f9cb02 100644 --- a/lib/redactor/codemirror/addon/fold/markdown-fold.js +++ b/lib/redactor/codemirror/addon/fold/markdown-fold.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/fold/xml-fold.js b/lib/redactor/codemirror/addon/fold/xml-fold.js index 13bc383..5450d37 100644 --- a/lib/redactor/codemirror/addon/fold/xml-fold.js +++ b/lib/redactor/codemirror/addon/fold/xml-fold.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/hint/anyword-hint.js b/lib/redactor/codemirror/addon/hint/anyword-hint.js index d27a9ec..b20d9f6 100644 --- a/lib/redactor/codemirror/addon/hint/anyword-hint.js +++ b/lib/redactor/codemirror/addon/hint/anyword-hint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/hint/css-hint.js b/lib/redactor/codemirror/addon/hint/css-hint.js index 980d119..5b36e82 100644 --- a/lib/redactor/codemirror/addon/hint/css-hint.js +++ b/lib/redactor/codemirror/addon/hint/css-hint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/hint/html-hint.js b/lib/redactor/codemirror/addon/hint/html-hint.js index d0cca4f..168f49c 100644 --- a/lib/redactor/codemirror/addon/hint/html-hint.js +++ b/lib/redactor/codemirror/addon/hint/html-hint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -98,6 +98,7 @@ dfn: s, dir: s, div: s, + dialog: { attrs: { open: null } }, dl: s, dt: s, em: s, diff --git a/lib/redactor/codemirror/addon/hint/javascript-hint.js b/lib/redactor/codemirror/addon/hint/javascript-hint.js index 182224d..9563979 100644 --- a/lib/redactor/codemirror/addon/hint/javascript-hint.js +++ b/lib/redactor/codemirror/addon/hint/javascript-hint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -69,7 +69,7 @@ function getCoffeeScriptToken(editor, cur) { // This getToken, it is for coffeescript, imitates the behavior of // getTokenAt method in javascript.js, that is, returning "property" - // type and treat "." as indepenent token. + // type and treat "." as independent token. var token = editor.getTokenAt(cur); if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') { token.end = token.start; @@ -144,12 +144,15 @@ base = base[context.pop().string]; if (base != null) gatherCompletions(base); } else { - // If not, just look in the global object and any local scope + // If not, just look in the global object, any local scope, and optional additional-context // (reading into JS mode internals to get at the local and global variables) for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); for (var c = token.state.context; c; c = c.prev) for (var v = c.vars; v; v = v.next) maybeAdd(v.name) for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name); + if (options && options.additionalContext != null) + for (var key in options.additionalContext) + maybeAdd(key); if (!options || options.useGlobalScope !== false) gatherCompletions(global); forEach(keywords, maybeAdd); diff --git a/lib/redactor/codemirror/addon/hint/show-hint.css b/lib/redactor/codemirror/addon/hint/show-hint.css index 5617ccc..d41031c 100644 --- a/lib/redactor/codemirror/addon/hint/show-hint.css +++ b/lib/redactor/codemirror/addon/hint/show-hint.css @@ -19,6 +19,7 @@ max-height: 20em; overflow-y: auto; + box-sizing: border-box; } .CodeMirror-hint { diff --git a/lib/redactor/codemirror/addon/hint/show-hint.js b/lib/redactor/codemirror/addon/hint/show-hint.js index 374cddf..eb448db 100644 --- a/lib/redactor/codemirror/addon/hint/show-hint.js +++ b/lib/redactor/codemirror/addon/hint/show-hint.js @@ -1,5 +1,7 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +// declare global: DOMRect (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -59,8 +61,10 @@ this.startPos = this.cm.getCursor("start"); this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; - var self = this; - cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); + if (this.options.updateOnCursorActivity) { + var self = this; + cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); + } } var requestAnimationFrame = window.requestAnimationFrame || function(fn) { @@ -73,7 +77,9 @@ if (!this.active()) return; this.cm.state.completionActive = null; this.tick = null; - this.cm.off("cursorActivity", this.activityFunc); + if (this.options.updateOnCursorActivity) { + this.cm.off("cursorActivity", this.activityFunc); + } if (this.widget && this.data) CodeMirror.signal(this.data, "close"); if (this.widget) this.widget.close(); @@ -85,12 +91,19 @@ }, pick: function(data, i) { - var completion = data.list[i]; - if (completion.hint) completion.hint(this.cm, data, completion); - else this.cm.replaceRange(getText(completion), completion.from || data.from, - completion.to || data.to, "complete"); - CodeMirror.signal(data, "pick", completion); - this.close(); + var completion = data.list[i], self = this; + this.cm.operation(function() { + if (completion.hint) + completion.hint(self.cm, data, completion); + else + self.cm.replaceRange(getText(completion), completion.from || data.from, + completion.to || data.to, "complete"); + CodeMirror.signal(data, "pick", completion); + self.cm.scrollIntoView(); + }); + if (this.options.closeOnPick) { + this.close(); + } }, cursorActivity: function() { @@ -99,9 +112,14 @@ this.debounce = 0; } + var identStart = this.startPos; + if(this.data) { + identStart = this.data.from; + } + var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || - pos.ch < this.startPos.ch || this.cm.somethingSelected() || + pos.ch < identStart.ch || this.cm.somethingSelected() || (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { this.close(); } else { @@ -206,6 +224,7 @@ } function Widget(completion, data) { + this.id = "cm-complete-" + Math.floor(Math.random(1e6)) this.completion = completion; this.data = data; this.picked = false; @@ -214,6 +233,9 @@ var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow; var hints = this.hints = ownerDocument.createElement("ul"); + hints.setAttribute("role", "listbox") + hints.setAttribute("aria-expanded", "true") + hints.id = this.id var theme = completion.cm.options.theme; hints.className = "CodeMirror-hints " + theme; this.selectedHint = data.selectedHint || 0; @@ -224,6 +246,9 @@ var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); if (cur.className != null) className = cur.className + " " + className; elt.className = className; + if (i == this.selectedHint) elt.setAttribute("aria-selected", "true") + elt.id = this.id + "-" + i + elt.setAttribute("role", "option") if (cur.render) cur.render(elt, data, cur); else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur))); elt.hintId = i; @@ -249,33 +274,36 @@ var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); container.appendChild(hints); - var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; - var scrolls = hints.scrollHeight > hints.clientHeight + 1 - var startScroll = cm.getScrollInfo(); + cm.getInputField().setAttribute("aria-autocomplete", "list") + cm.getInputField().setAttribute("aria-owns", this.id) + cm.getInputField().setAttribute("aria-activedescendant", this.id + "-" + this.selectedHint) - if (overlapY > 0) { - var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); - if (curTop - height > 0) { // Fits above cursor - hints.style.top = (top = pos.top - height - offsetTop) + "px"; + var box = completion.options.moveOnOverlap ? hints.getBoundingClientRect() : new DOMRect(); + var scrolls = completion.options.paddingForScrollbar ? hints.scrollHeight > hints.clientHeight + 1 : false; + + // Compute in the timeout to avoid reflow on init + var startScroll; + setTimeout(function() { startScroll = cm.getScrollInfo(); }); + + var overlapY = box.bottom - winH; + if (overlapY > 0) { // Does not fit below + var height = box.bottom - box.top, spaceAbove = box.top - (pos.bottom - pos.top) - 2 + if (winH - box.top < spaceAbove) { // More room at the top + if (height > spaceAbove) hints.style.height = (height = spaceAbove) + "px"; + hints.style.top = ((top = pos.top - height) - offsetTop) + "px"; below = false; - } else if (height > winH) { - hints.style.height = (winH - 5) + "px"; - hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px"; - var cursor = cm.getCursor(); - if (data.from.ch != cursor.ch) { - pos = cm.cursorCoords(cursor); - hints.style.left = (left = pos.left - offsetLeft) + "px"; - box = hints.getBoundingClientRect(); - } + } else { + hints.style.height = (winH - box.top - 2) + "px"; } } var overlapX = box.right - winW; + if (scrolls) overlapX += cm.display.nativeBarWidth; if (overlapX > 0) { if (box.right - box.left > winW) { hints.style.width = (winW - 5) + "px"; overlapX -= (box.right - box.left) - winW; } - hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px"; + hints.style.left = (left = Math.max(pos.left - overlapX - offsetLeft, 0)) + "px"; } if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) node.style.paddingRight = cm.display.nativeBarWidth + "px" @@ -298,6 +326,7 @@ cm.on("scroll", this.onScroll = function() { var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); + if (!startScroll) startScroll = cm.getScrollInfo(); var newTop = top + startScroll.top - curScroll.top; var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop); if (!below) point += hints.offsetHeight; @@ -322,7 +351,12 @@ CodeMirror.on(hints, "mousedown", function() { setTimeout(function(){cm.focus();}, 20); }); - this.scrollToActive() + + // The first hint doesn't need to be scrolled to on init + var selectedHintRange = this.getSelectedHintRange(); + if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) { + this.scrollToActive(); + } CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); return true; @@ -332,8 +366,11 @@ close: function() { if (this.completion.widget != this) return; this.completion.widget = null; - this.hints.parentNode.removeChild(this.hints); + if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints); this.completion.cm.removeKeyMap(this.keyMap); + var input = this.completion.cm.getInputField() + input.removeAttribute("aria-activedescendant") + input.removeAttribute("aria-owns") var cm = this.completion.cm; if (this.completion.options.closeOnUnfocus) { @@ -361,23 +398,39 @@ i = avoidWrap ? 0 : this.data.list.length - 1; if (this.selectedHint == i) return; var node = this.hints.childNodes[this.selectedHint]; - if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); + if (node) { + node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); + node.removeAttribute("aria-selected") + } node = this.hints.childNodes[this.selectedHint = i]; node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; + node.setAttribute("aria-selected", "true") + this.completion.cm.getInputField().setAttribute("aria-activedescendant", node.id) this.scrollToActive() CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); }, scrollToActive: function() { - var node = this.hints.childNodes[this.selectedHint] - if (node.offsetTop < this.hints.scrollTop) - this.hints.scrollTop = node.offsetTop - 3; - else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) - this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; + var selectedHintRange = this.getSelectedHintRange(); + var node1 = this.hints.childNodes[selectedHintRange.from]; + var node2 = this.hints.childNodes[selectedHintRange.to]; + var firstNode = this.hints.firstChild; + if (node1.offsetTop < this.hints.scrollTop) + this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop; + else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) + this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop; }, screenAmount: function() { return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; + }, + + getSelectedHintRange: function() { + var margin = this.completion.options.scrollMargin || 0; + return { + from: Math.max(0, this.selectedHint - margin), + to: Math.min(this.data.list.length - 1, this.selectedHint + margin), + }; } }; @@ -455,11 +508,15 @@ completeSingle: true, alignWithWord: true, closeCharacters: /[\s()\[\]{};:>,]/, + closeOnPick: true, closeOnUnfocus: true, + updateOnCursorActivity: true, completeOnSingleClick: true, container: null, customKeys: null, - extraKeys: null + extraKeys: null, + paddingForScrollbar: true, + moveOnOverlap: true, }; CodeMirror.defineOption("hintOptions", null); diff --git a/lib/redactor/codemirror/addon/hint/sql-hint.js b/lib/redactor/codemirror/addon/hint/sql-hint.js index 444eba8..5c98105 100644 --- a/lib/redactor/codemirror/addon/hint/sql-hint.js +++ b/lib/redactor/codemirror/addon/hint/sql-hint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -23,16 +23,16 @@ function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" } + function getModeConf(editor, field) { + return editor.getModeAt(editor.getCursor()).config[field] || CodeMirror.resolveMode("text/x-sql")[field] + } + function getKeywords(editor) { - var mode = editor.doc.modeOption; - if (mode === "sql") mode = "text/x-sql"; - return CodeMirror.resolveMode(mode).keywords; + return getModeConf(editor, "keywords") || [] } function getIdentifierQuote(editor) { - var mode = editor.doc.modeOption; - if (mode === "sql") mode = "text/x-sql"; - return CodeMirror.resolveMode(mode).identifierQuote || "`"; + return getModeConf(editor, "identifierQuote") || "`"; } function getText(item) { @@ -97,7 +97,7 @@ if (name.charAt(0) == ".") { name = name.substr(1); } - // replace doublicated identifierQuotes with single identifierQuotes + // replace duplicated identifierQuotes with single identifierQuotes // and remove single identifierQuotes var nameParts = name.split(identifierQuote+identifierQuote); for (var i = 0; i < nameParts.length; i++) @@ -109,9 +109,9 @@ var nameParts = getText(name).split("."); for (var i = 0; i < nameParts.length; i++) nameParts[i] = identifierQuote + - // doublicate identifierQuotes - nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) + - identifierQuote; + // duplicate identifierQuotes + nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) + + identifierQuote; var escaped = nameParts.join("."); if (typeof name == "string") return escaped; name = shallowClone(name); @@ -187,7 +187,7 @@ function eachWord(lineText, f) { var words = lineText.split(/\s+/) for (var i = 0; i < words.length; i++) - if (words[i]) f(words[i].replace(/[,;]/g, '')) + if (words[i]) f(words[i].replace(/[`,;]/g, '')) } function findTableByAlias(alias, editor) { @@ -264,7 +264,7 @@ token.string = token.string.slice(0, cur.ch - token.start); } - if (token.string.match(/^[.`"\w@]\w*$/)) { + if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) { search = token.string; start = token.start; end = token.end; @@ -283,21 +283,21 @@ } return w; }; - addMatches(result, search, defaultTable, function(w) { + addMatches(result, search, defaultTable, function(w) { return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table"); - }); - addMatches( + }); + addMatches( result, search, tables, function(w) { return objectOrClass(w, "CodeMirror-hint-table"); } - ); - if (!disableKeywords) - addMatches(result, search, keywords, function(w) { + ); + if (!disableKeywords) + addMatches(result, search, keywords, function(w) { return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword"); - }); - } + }); + } return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)}; }); diff --git a/lib/redactor/codemirror/addon/hint/xml-hint.js b/lib/redactor/codemirror/addon/hint/xml-hint.js index 7575b37..1fc6394 100644 --- a/lib/redactor/codemirror/addon/hint/xml-hint.js +++ b/lib/redactor/codemirror/addon/hint/xml-hint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -101,8 +101,14 @@ } replaceToken = true; } - for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle)) - result.push(quote + atValues[i] + quote); + var returnHintsFromAtValues = function(atValues) { + if (atValues) + for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle)) + result.push(quote + atValues[i] + quote); + return returnHints(); + }; + if (atValues && atValues.then) return atValues.then(returnHintsFromAtValues); + return returnHintsFromAtValues(atValues); } else { // An attribute name if (token.type == "attribute") { prefix = token.string; @@ -112,11 +118,14 @@ result.push(attr); } } - return { - list: result, - from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur, - to: replaceToken ? Pos(cur.line, token.end) : cur - }; + function returnHints() { + return { + list: result, + from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur, + to: replaceToken ? Pos(cur.line, token.end) : cur + }; + } + return returnHints(); } CodeMirror.registerHelper("hint", "xml", getHints); diff --git a/lib/redactor/codemirror/addon/lint/coffeescript-lint.js b/lib/redactor/codemirror/addon/lint/coffeescript-lint.js index a54c703..793ea90 100644 --- a/lib/redactor/codemirror/addon/lint/coffeescript-lint.js +++ b/lib/redactor/codemirror/addon/lint/coffeescript-lint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js diff --git a/lib/redactor/codemirror/addon/lint/css-lint.js b/lib/redactor/codemirror/addon/lint/css-lint.js index 6058a73..94a67b4 100644 --- a/lib/redactor/codemirror/addon/lint/css-lint.js +++ b/lib/redactor/codemirror/addon/lint/css-lint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Depends on csslint.js from https://github.com/stubbornella/csslint diff --git a/lib/redactor/codemirror/addon/lint/html-lint.js b/lib/redactor/codemirror/addon/lint/html-lint.js index 5295c33..5be24fe 100644 --- a/lib/redactor/codemirror/addon/lint/html-lint.js +++ b/lib/redactor/codemirror/addon/lint/html-lint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js diff --git a/lib/redactor/codemirror/addon/lint/javascript-lint.js b/lib/redactor/codemirror/addon/lint/javascript-lint.js index cc132d7..c1599f8 100644 --- a/lib/redactor/codemirror/addon/lint/javascript-lint.js +++ b/lib/redactor/codemirror/addon/lint/javascript-lint.js @@ -1,5 +1,7 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +// Depends on jshint.js from https://github.com/jshint/jshint (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/lint/json-lint.js b/lib/redactor/codemirror/addon/lint/json-lint.js index ac1d6ec..4a4fe39 100644 --- a/lib/redactor/codemirror/addon/lint/json-lint.js +++ b/lib/redactor/codemirror/addon/lint/json-lint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Depends on jsonlint.js from https://github.com/zaach/jsonlint diff --git a/lib/redactor/codemirror/addon/lint/lint.css b/lib/redactor/codemirror/addon/lint/lint.css index f097cfe..e1560db 100644 --- a/lib/redactor/codemirror/addon/lint/lint.css +++ b/lib/redactor/codemirror/addon/lint/lint.css @@ -25,22 +25,20 @@ -ms-transition: opacity .4s; } -.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { +.CodeMirror-lint-mark { background-position: left bottom; background-repeat: repeat-x; } -.CodeMirror-lint-mark-error { - background-image: - url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") - ; -} - .CodeMirror-lint-mark-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); } -.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { +.CodeMirror-lint-mark-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg=="); +} + +.CodeMirror-lint-marker { background-position: center center; background-repeat: no-repeat; cursor: pointer; @@ -51,23 +49,31 @@ position: relative; } -.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { +.CodeMirror-lint-message { padding-left: 18px; background-position: top left; background-repeat: no-repeat; } -.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); -} - .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); } +.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); +} + .CodeMirror-lint-marker-multiple { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; } + +.CodeMirror-lint-line-error { + background-color: rgba(183, 76, 81, 0.08); +} + +.CodeMirror-lint-line-warning { + background-color: rgba(255, 211, 0, 0.1); +} diff --git a/lib/redactor/codemirror/addon/lint/lint.js b/lib/redactor/codemirror/addon/lint/lint.js index aa75ba0..21631b9 100644 --- a/lib/redactor/codemirror/addon/lint/lint.js +++ b/lib/redactor/codemirror/addon/lint/lint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -11,17 +11,23 @@ })(function(CodeMirror) { "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; - function showTooltip(e, content) { + function showTooltip(cm, e, content) { var tt = document.createElement("div"); - tt.className = "CodeMirror-lint-tooltip"; + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; tt.appendChild(content.cloneNode(true)); - document.body.appendChild(tt); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); function position(e) { if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); - tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; - tt.style.left = (e.clientX + 5) + "px"; + var top = Math.max(0, e.clientY - tt.offsetHeight - 5); + var left = Math.max(0, Math.min(e.clientX + 5, tt.ownerDocument.defaultView.innerWidth - tt.offsetWidth)); + tt.style.top = top + "px" + tt.style.left = left + "px"; } CodeMirror.on(document, "mousemove", position); position(e); @@ -38,8 +44,8 @@ setTimeout(function() { rm(tt); }, 600); } - function showTooltipFor(e, content, node) { - var tooltip = showTooltip(e, content); + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); function hide() { CodeMirror.off(node, "mouseout", hide); if (tooltip) { hideTooltip(tooltip); tooltip = null; } @@ -55,39 +61,64 @@ CodeMirror.on(node, "mouseout", hide); } - function LintState(cm, options, hasGutter) { + function LintState(cm, conf, hasGutter) { this.marked = []; - this.options = options; + if (conf instanceof Function) conf = {getAnnotations: conf}; + if (!conf || conf === true) conf = {}; + this.options = {}; + this.linterOptions = conf.options || {}; + for (var prop in defaults) this.options[prop] = defaults[prop]; + for (var prop in conf) { + if (defaults.hasOwnProperty(prop)) { + if (conf[prop] != null) this.options[prop] = conf[prop]; + } else if (!conf.options) { + this.linterOptions[prop] = conf[prop]; + } + } this.timeout = null; this.hasGutter = hasGutter; this.onMouseOver = function(e) { onMouseOver(cm, e); }; this.waitingFor = 0 } - function parseOptions(_cm, options) { - if (options instanceof Function) return {getAnnotations: options}; - if (!options || options === true) options = {}; - return options; + var defaults = { + highlightLines: false, + tooltips: true, + delay: 500, + lintOnChange: true, + getAnnotations: null, + async: false, + selfContain: null, + formatAnnotation: null, + onUpdateLinting: null } function clearMarks(cm) { var state = cm.state.lint; if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (state.options.highlightLines) clearErrorLines(cm); for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); state.marked.length = 0; } - function makeMarker(labels, severity, multiple, tooltips) { + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { var marker = document.createElement("div"), inner = marker; - marker.className = "CodeMirror-lint-marker-" + severity; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; if (multiple) { inner = marker.appendChild(document.createElement("div")); - inner.className = "CodeMirror-lint-marker-multiple"; + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; } if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { - showTooltipFor(e, labels, inner); + showTooltipFor(cm, e, labels, inner); }); return marker; @@ -111,16 +142,16 @@ var severity = ann.severity; if (!severity) severity = "error"; var tip = document.createElement("div"); - tip.className = "CodeMirror-lint-message-" + severity; + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; if (typeof ann.messageHTML != 'undefined') { - tip.innerHTML = ann.messageHTML; + tip.innerHTML = ann.messageHTML; } else { - tip.appendChild(document.createTextNode(ann.message)); + tip.appendChild(document.createTextNode(ann.message)); } return tip; } - function lintAsync(cm, getAnnotations, passOptions) { + function lintAsync(cm, getAnnotations) { var state = cm.state.lint var id = ++state.waitingFor function abort() { @@ -133,22 +164,23 @@ if (state.waitingFor != id) return if (arg2 && annotations instanceof CodeMirror) annotations = arg2 cm.operation(function() {updateLinting(cm, annotations)}) - }, passOptions, cm); + }, state.linterOptions, cm); } function startLinting(cm) { - var state = cm.state.lint, options = state.options; + var state = cm.state.lint; + if (!state) return; + var options = state.options; /* * Passing rules in `options` property prevents JSHint (and other linters) from complaining * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. */ - var passOptions = options.options || options; var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); if (!getAnnotations) return; if (options.async || getAnnotations.async) { - lintAsync(cm, getAnnotations, passOptions) + lintAsync(cm, getAnnotations) } else { - var annotations = getAnnotations(cm.getValue(), passOptions, cm); + var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); if (!annotations) return; if (annotations.then) annotations.then(function(issues) { cm.operation(function() {updateLinting(cm, issues)}) @@ -158,8 +190,10 @@ } function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; clearMarks(cm); - var state = cm.state.lint, options = state.options; var annotations = groupByLine(annotationsNotSorted); @@ -180,14 +214,16 @@ if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { - className: "CodeMirror-lint-mark-" + severity, + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, __annotation: ann })); } - if (state.hasGutter) - cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, - state.options.tooltips)); + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1, + options.tooltips)); + + if (options.highlightLines) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); } if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); } @@ -196,17 +232,17 @@ var state = cm.state.lint; if (!state) return; clearTimeout(state.timeout); - state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); } - function popupTooltips(annotations, e) { + function popupTooltips(cm, annotations, e) { var target = e.target || e.srcElement; var tooltip = document.createDocumentFragment(); for (var i = 0; i < annotations.length; i++) { var ann = annotations[i]; tooltip.appendChild(annotationTooltip(ann)); } - showTooltipFor(e, tooltip, target); + showTooltipFor(cm, e, tooltip, target); } function onMouseOver(cm, e) { @@ -220,7 +256,7 @@ var ann = spans[i].__annotation; if (ann) annotations.push(ann); } - if (annotations.length) popupTooltips(annotations, e); + if (annotations.length) popupTooltips(cm, annotations, e); } CodeMirror.defineOption("lint", false, function(cm, val, old) { @@ -236,8 +272,8 @@ if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; - var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); - if (state.options.lintOnChange !== false) + var state = cm.state.lint = new LintState(cm, val, hasLintGutter); + if (state.options.lintOnChange) cm.on("change", onChange); if (state.options.tooltips != false && state.options.tooltips != "gutter") CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); @@ -247,6 +283,6 @@ }); CodeMirror.defineExtension("performLint", function() { - if (this.state.lint) startLinting(this); + startLinting(this); }); }); diff --git a/lib/redactor/codemirror/addon/lint/yaml-lint.js b/lib/redactor/codemirror/addon/lint/yaml-lint.js index b4ac5ab..5dbb159 100644 --- a/lib/redactor/codemirror/addon/lint/yaml-lint.js +++ b/lib/redactor/codemirror/addon/lint/yaml-lint.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/merge/merge.js b/lib/redactor/codemirror/addon/merge/merge.js index 8296540..14362fa 100644 --- a/lib/redactor/codemirror/addon/merge/merge.js +++ b/lib/redactor/codemirror/addon/merge/merge.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL @@ -44,6 +44,7 @@ this.orig.state.trackAlignable = new TrackAlignable(this.orig) } this.lockButton.title = this.edit.phrase("Toggle locked scrolling"); + this.lockButton.setAttribute("aria-label", this.lockButton.title); this.orig.state.diffViews = [this]; var classLocation = options.chunkClassLocation || "background"; @@ -443,22 +444,26 @@ aligners[i].clear(); aligners.length = 0; - var cm = [dv.edit, dv.orig], scroll = []; + var cm = [dv.edit, dv.orig], scroll = [], offset = [] if (other) cm.push(other.orig); - for (var i = 0; i < cm.length; i++) + for (var i = 0; i < cm.length; i++) { scroll.push(cm[i].getScrollInfo().top); + offset.push(-cm[i].getScrollerElement().getBoundingClientRect().top) + } + if (offset[0] != offset[1] || cm.length == 3 && offset[1] != offset[2]) + alignLines(cm, offset, [0, 0, 0], aligners) for (var ln = 0; ln < linesToAlign.length; ln++) - alignLines(cm, linesToAlign[ln], aligners); + alignLines(cm, offset, linesToAlign[ln], aligners); for (var i = 0; i < cm.length; i++) cm[i].scrollTo(null, scroll[i]); } - function alignLines(cm, lines, aligners) { - var maxOffset = 0, offset = []; + function alignLines(cm, cmOffset, lines, aligners) { + var maxOffset = -1e8, offset = []; for (var i = 0; i < cm.length; i++) if (lines[i] != null) { - var off = cm[i].heightAtLine(lines[i], "local"); + var off = cm[i].heightAtLine(lines[i], "local") - cmOffset[i]; offset[i] = off; maxOffset = Math.max(maxOffset, off); } @@ -504,6 +509,9 @@ copy.title = dv.edit.phrase(editOriginals ? "Push to left" : "Revert chunk"); copy.chunk = chunk; copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px"; + copy.setAttribute("role", "button"); + copy.setAttribute("tabindex", "0"); + copy.setAttribute("aria-label", copy.title); if (editOriginals) { var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit; @@ -514,6 +522,9 @@ origFrom: chunk.editFrom, origTo: chunk.editTo}; copyReverse.style.top = topReverse + "px"; dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px"; + copyReverse.setAttribute("role", "button"); + copyReverse.setAttribute("tabindex", "0"); + copyReverse.setAttribute("aria-label", copyReverse.title); } } } @@ -595,12 +606,15 @@ function buildGap(dv) { var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock"); + lock.setAttribute("role", "button"); + lock.setAttribute("tabindex", "0"); var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); + CodeMirror.on(lock, "keyup", function(e) { (e.key === "Enter" || e.code === "Space") && setScrollLock(dv, !dv.lockScroll); }); var gapElts = [lockWrap]; if (dv.mv.options.revertButtons !== false) { dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); - CodeMirror.on(dv.copyButtons, "click", function(e) { + var copyButtons = function(e) { var node = e.target || e.srcElement; if (!node.chunk) return; if (node.className == "CodeMirror-merge-copy-reverse") { @@ -608,7 +622,9 @@ return; } copyChunk(dv, dv.edit, dv.orig, node.chunk); - }); + } + CodeMirror.on(dv.copyButtons, "click", copyButtons); + CodeMirror.on(dv.copyButtons, "keyup", function(e) { (e.key === "Enter" || e.code === "Space") && copyButtons(e); }); gapElts.unshift(dv.copyButtons); } if (dv.mv.options.connect != "align") { diff --git a/lib/redactor/codemirror/addon/mode/loadmode.js b/lib/redactor/codemirror/addon/mode/loadmode.js index 4ce716a..ee1c151 100644 --- a/lib/redactor/codemirror/addon/mode/loadmode.js +++ b/lib/redactor/codemirror/addon/mode/loadmode.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -16,8 +16,8 @@ var countDown = n; return function() { if (--countDown == 0) cont(); }; } - function ensureDeps(mode, cont) { - var deps = CodeMirror.modes[mode].dependencies; + function ensureDeps(mode, cont, options) { + var modeObj = CodeMirror.modes[mode], deps = modeObj && modeObj.dependencies; if (!deps) return cont(); var missing = []; for (var i = 0; i < deps.length; ++i) { @@ -27,16 +27,18 @@ if (!missing.length) return cont(); var split = splitCallback(cont, missing.length); for (var i = 0; i < missing.length; ++i) - CodeMirror.requireMode(missing[i], split); + CodeMirror.requireMode(missing[i], split, options); } - CodeMirror.requireMode = function(mode, cont) { + CodeMirror.requireMode = function(mode, cont, options) { if (typeof mode != "string") mode = mode.name; - if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); + if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont, options); if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); - var file = CodeMirror.modeURL.replace(/%N/g, mode); - if (env == "plain") { + var file = options && options.path ? options.path(mode) : CodeMirror.modeURL.replace(/%N/g, mode); + if (options && options.loadMode) { + options.loadMode(file, function() { ensureDeps(mode, cont, options) }) + } else if (env == "plain") { var script = document.createElement("script"); script.src = file; var others = document.getElementsByTagName("script")[0]; @@ -44,7 +46,7 @@ CodeMirror.on(script, "load", function() { ensureDeps(mode, function() { for (var i = 0; i < list.length; ++i) list[i](); - }); + }, options); }); others.parentNode.insertBefore(script, others); } else if (env == "cjs") { @@ -55,10 +57,10 @@ } }; - CodeMirror.autoLoadMode = function(instance, mode) { + CodeMirror.autoLoadMode = function(instance, mode, options) { if (!CodeMirror.modes.hasOwnProperty(mode)) CodeMirror.requireMode(mode, function() { instance.setOption("mode", instance.getOption("mode")); - }); + }, options); }; }); diff --git a/lib/redactor/codemirror/addon/mode/multiplex.js b/lib/redactor/codemirror/addon/mode/multiplex.js index 93fd9a5..d7f378a 100644 --- a/lib/redactor/codemirror/addon/mode/multiplex.js +++ b/lib/redactor/codemirror/addon/mode/multiplex.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -12,7 +12,7 @@ "use strict"; CodeMirror.multiplexingMode = function(outer /*, others */) { - // Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects + // Others should be {open, close, mode [, delimStyle] [, innerStyle] [, parseDelimiters]} objects var others = Array.prototype.slice.call(arguments, 1); function indexOf(string, pattern, from, returnEnd) { @@ -29,7 +29,8 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return { outer: CodeMirror.startState(outer), innerActive: null, - inner: null + inner: null, + startingInner: false }; }, @@ -37,7 +38,8 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return { outer: CodeMirror.copyState(outer, state.outer), innerActive: state.innerActive, - inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner) + inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner), + startingInner: state.startingInner }; }, @@ -49,6 +51,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { var found = indexOf(oldContent, other.open, stream.pos); if (found == stream.pos) { if (!other.parseDelimiters) stream.match(other.open); + state.startingInner = !!other.parseDelimiters state.innerActive = other; // Get the outer indent, making sure to handle CodeMirror.Pass @@ -74,7 +77,8 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { state.innerActive = state.inner = null; return this.token(stream, state); } - var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) : -1; + var found = curInner.close && !state.startingInner ? + indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) : -1; if (found == stream.pos && !curInner.parseDelimiters) { stream.match(curInner.close); state.innerActive = state.inner = null; @@ -83,6 +87,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { if (found > -1) stream.string = oldContent.slice(0, found); var innerToken = curInner.mode.token(stream, state.inner); if (found > -1) stream.string = oldContent; + else if (stream.pos > stream.start) state.startingInner = false if (found == stream.pos && curInner.parseDelimiters) state.innerActive = state.inner = null; diff --git a/lib/redactor/codemirror/addon/mode/multiplex_test.js b/lib/redactor/codemirror/addon/mode/multiplex_test.js index c51cad4..37e8077 100644 --- a/lib/redactor/codemirror/addon/mode/multiplex_test.js +++ b/lib/redactor/codemirror/addon/mode/multiplex_test.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function() { CodeMirror.defineMode("markdown_with_stex", function(){ @@ -30,4 +30,20 @@ MT( "stexInsideMarkdown", "[strong **Equation:**] [delim&delim-open $][inner&tag \\pi][delim&delim-close $]"); + + CodeMirror.defineMode("identical_delim_multiplex", function() { + return CodeMirror.multiplexingMode(CodeMirror.getMode({indentUnit: 2}, "javascript"), { + open: "#", + close: "#", + mode: CodeMirror.getMode({}, "markdown"), + parseDelimiters: true, + innerStyle: "q" + }); + }); + + var mode2 = CodeMirror.getMode({}, "identical_delim_multiplex"); + + test.mode("identical_delimiters_with_parseDelimiters", mode2, [ + "[keyword let] [def x] [operator =] [q #foo][q&em *bar*][q #];" + ], "multiplexing") })(); diff --git a/lib/redactor/codemirror/addon/mode/overlay.js b/lib/redactor/codemirror/addon/mode/overlay.js index 016e3c2..1aab159 100644 --- a/lib/redactor/codemirror/addon/mode/overlay.js +++ b/lib/redactor/codemirror/addon/mode/overlay.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Utility function that allows modes to be combined. The mode given // as the base argument takes care of most of the normal mode diff --git a/lib/redactor/codemirror/addon/mode/simple.js b/lib/redactor/codemirror/addon/mode/simple.js index 655f991..1f0e0be 100644 --- a/lib/redactor/codemirror/addon/mode/simple.js +++ b/lib/redactor/codemirror/addon/mode/simple.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -68,6 +68,7 @@ var flags = ""; if (val instanceof RegExp) { if (val.ignoreCase) flags = "i"; + if (val.unicode) flags += "u" val = val.source; } else { val = String(val); @@ -137,10 +138,9 @@ var token = rule.token if (token && token.apply) token = token(matches) if (matches.length > 2 && rule.token && typeof rule.token != "string") { - state.pending = []; for (var j = 2; j < matches.length; j++) if (matches[j]) - state.pending.push({text: matches[j], token: rule.token[j - 1]}); + (state.pending || (state.pending = [])).push({text: matches[j], token: rule.token[j - 1]}); stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0)); return token[0]; } else if (token && token.join) { diff --git a/lib/redactor/codemirror/addon/runmode/colorize.js b/lib/redactor/codemirror/addon/runmode/colorize.js index 3be5411..ead1c58 100644 --- a/lib/redactor/codemirror/addon/runmode/colorize.js +++ b/lib/redactor/codemirror/addon/runmode/colorize.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -31,7 +31,7 @@ var text = []; textContent(node, text); - node.innerHTML = ""; + node.textContent = ""; CodeMirror.runMode(text.join(""), mode, node); node.className += " cm-s-default"; diff --git a/lib/redactor/codemirror/addon/runmode/runmode-standalone.js b/lib/redactor/codemirror/addon/runmode/runmode-standalone.js index 745eaf8..2f4db3d 100644 --- a/lib/redactor/codemirror/addon/runmode/runmode-standalone.js +++ b/lib/redactor/codemirror/addon/runmode/runmode-standalone.js @@ -1,158 +1,334 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +(function () { + 'use strict'; -window.CodeMirror = {}; + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (Object.prototype.hasOwnProperty.call(obj, prop) && (overwrite !== false || !Object.prototype.hasOwnProperty.call(target, prop))) + { target[prop] = obj[prop]; } } + return target + } -(function() { -"use strict"; + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } -function splitLines(string){ return string.split(/\r?\n|\r/); }; + function nothing() {} -function StringStream(string) { - this.pos = this.start = 0; - this.string = string; - this.lineStart = 0; -} -StringStream.prototype = { - eol: function() {return this.pos >= this.string.length;}, - sol: function() {return this.pos == 0;}, - peek: function() {return this.string.charAt(this.pos) || null;}, - next: function() { + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { if (this.pos < this.string.length) - return this.string.charAt(this.pos++); - }, - eat: function(match) { + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { var ch = this.string.charAt(this.pos); - if (typeof match == "string") var ok = ch == match; - else var ok = ch && (match.test ? match.test(ch) : match(ch)); - if (ok) {++this.pos; return ch;} - }, - eatWhile: function(match) { + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { var start = this.pos; while (this.eat(match)){} - return this.pos > start; - }, - eatSpace: function() { + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { var start = this.pos; - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; - return this.pos > start; - }, - skipToEnd: function() {this.pos = this.string.length;}, - skipTo: function(ch) { + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { var found = this.string.indexOf(ch, this.pos); - if (found > -1) {this.pos = found; return true;} - }, - backUp: function(n) {this.pos -= n;}, - column: function() {return this.start - this.lineStart;}, - indentation: function() {return 0;}, - match: function(pattern, consume, caseInsensitive) { + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { if (typeof pattern == "string") { - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; var substr = this.string.substr(this.pos, pattern.length); if (cased(substr) == cased(pattern)) { - if (consume !== false) this.pos += pattern.length; - return true; + if (consume !== false) { this.pos += pattern.length; } + return true } } else { var match = this.string.slice(this.pos).match(pattern); - if (match && match.index > 0) return null; - if (match && consume !== false) this.pos += match[0].length; - return match; + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match } - }, - current: function(){return this.string.slice(this.start, this.pos);}, - hideFirstChars: function(n, inner) { + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { this.lineStart += n; - try { return inner(); } + try { return inner() } finally { this.lineStart -= n; } - }, - lookAhead: function() { return null } -}; -CodeMirror.StringStream = StringStream; + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; -CodeMirror.startState = function (mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true; -}; + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; -var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; -CodeMirror.defineMode = function (name, mode) { - if (arguments.length > 2) - mode.dependencies = Array.prototype.slice.call(arguments, 2); - modes[name] = mode; -}; -CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; }; -CodeMirror.resolveMode = function(spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { - spec = mimeModes[spec]; - } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { - spec = mimeModes[spec.name]; - } - if (typeof spec == "string") return {name: spec}; - else return spec || {name: "null"}; -}; -CodeMirror.getMode = function (options, spec) { - spec = CodeMirror.resolveMode(spec); - var mfactory = modes[spec.name]; - if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, spec); -}; -CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min; -CodeMirror.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd();}}; -}); -CodeMirror.defineMIME("text/plain", "null"); - -CodeMirror.runMode = function (string, modespec, callback, options) { - var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec); - - if (callback.nodeType == 1) { - var tabSize = (options && options.tabSize) || 4; - var node = callback, col = 0; - node.innerHTML = ""; - callback = function (text, style) { - if (text == "\n") { - node.appendChild(document.createElement("br")); - col = 0; - return; - } - var content = ""; - // replace tabs - for (var pos = 0; ;) { - var idx = text.indexOf("\t", pos); - if (idx == -1) { - content += text.slice(pos); - col += text.length - pos; - break; - } else { - col += idx - pos; - content += text.slice(pos, idx); - var size = tabSize - col % tabSize; - col += size; - for (var i = 0; i < size; ++i) content += " "; - pos = idx + 1; - } - } - - if (style) { - var sp = node.appendChild(document.createElement("span")); - sp.className = "cm-" + style.replace(/ +/g, " cm-"); - sp.appendChild(document.createTextNode(content)); - } else { - node.appendChild(document.createTextNode(content)); - } - }; + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; } - var lines = splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); - for (var i = 0, e = lines.length; i < e; ++i) { - if (i) callback("\n"); - var stream = new CodeMirror.StringStream(lines[i]); - if (!stream.string && mode.blankLine) mode.blankLine(state); - while (!stream.eol()) { - var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start, state); - stream.start = stream.pos; + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } } -}; -})(); + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + var modeMethods = { + __proto__: null, + modes: modes, + mimeModes: mimeModes, + defineMode: defineMode, + defineMIME: defineMIME, + resolveMode: resolveMode, + getMode: getMode, + modeExtensions: modeExtensions, + extendMode: extendMode, + copyState: copyState, + innerMode: innerMode, + startState: startState + }; + + // declare global: globalThis, CodeMirror + + // Create a minimal CodeMirror needed to use runMode, and assign to root. + var root = typeof globalThis !== 'undefined' ? globalThis : window; + root.CodeMirror = {}; + + // Copy StringStream and mode methods into CodeMirror object. + CodeMirror.StringStream = StringStream; + for (var exported in modeMethods) { CodeMirror[exported] = modeMethods[exported]; } + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min; + CodeMirror.splitLines = function(string) { return string.split(/\r?\n|\r/) }; + CodeMirror.countColumn = countColumn; + + CodeMirror.defaults = { indentUnit: 2 }; + + // CodeMirror, copyright (c) by Marijn Haverbeke and others + // Distributed under an MIT license: https://codemirror.net/5/LICENSE + + (function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + { mod(require("../../lib/codemirror")); } + else if (typeof define == "function" && define.amd) // AMD + { define(["../../lib/codemirror"], mod); } + else // Plain browser env + { mod(CodeMirror); } + })(function(CodeMirror) { + + CodeMirror.runMode = function(string, modespec, callback, options) { + var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); + var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; + + // Create a tokenizing callback function if passed-in callback is a DOM element. + if (callback.appendChild) { + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); + var node = callback, col = 0; + node.textContent = ""; + callback = function(text, style) { + if (text == "\n") { + // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. + // Emitting a carriage return makes everything ok. + node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text)); + col = 0; + return; + } + var content = ""; + // replace tabs + for (var pos = 0;;) { + var idx = text.indexOf("\t", pos); + if (idx == -1) { + content += text.slice(pos); + col += text.length - pos; + break; + } else { + col += idx - pos; + content += text.slice(pos, idx); + var size = tabSize - col % tabSize; + col += size; + for (var i = 0; i < size; ++i) { content += " "; } + pos = idx + 1; + } + } + // Create a node with token style and append it to the callback DOM element. + if (style) { + var sp = node.appendChild(document.createElement("span")); + sp.className = "cm-" + style.replace(/ +/g, " cm-"); + sp.appendChild(document.createTextNode(content)); + } else { + node.appendChild(document.createTextNode(content)); + } + }; + } + + var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) { callback("\n"); } + var stream = new CodeMirror.StringStream(lines[i], null, { + lookAhead: function(n) { return lines[i + n] }, + baseToken: function() {} + }); + if (!stream.string && mode.blankLine) { mode.blankLine(state); } + while (!stream.eol()) { + var style = mode.token(stream, state); + callback(stream.current(), style, i, stream.start, state, mode); + stream.start = stream.pos; + } + } + }; + + }); + +}()); diff --git a/lib/redactor/codemirror/addon/runmode/runmode.js b/lib/redactor/codemirror/addon/runmode/runmode.js index eb4cadf..e1525b6 100644 --- a/lib/redactor/codemirror/addon/runmode/runmode.js +++ b/lib/redactor/codemirror/addon/runmode/runmode.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -13,13 +13,14 @@ CodeMirror.runMode = function(string, modespec, callback, options) { var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); - var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); + var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; + // Create a tokenizing callback function if passed-in callback is a DOM element. if (callback.appendChild) { - var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); var node = callback, col = 0; - node.innerHTML = ""; + node.textContent = ""; callback = function(text, style) { if (text == "\n") { // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. @@ -45,7 +46,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) { pos = idx + 1; } } - + // Create a node with token style and append it to the callback DOM element. if (style) { var sp = node.appendChild(document.createElement("span")); sp.className = "cm-" + style.replace(/ +/g, " cm-"); @@ -59,11 +60,14 @@ CodeMirror.runMode = function(string, modespec, callback, options) { var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); - var stream = new CodeMirror.StringStream(lines[i]); + var stream = new CodeMirror.StringStream(lines[i], null, { + lookAhead: function(n) { return lines[i + n] }, + baseToken: function() {} + }); if (!stream.string && mode.blankLine) mode.blankLine(state); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start, state); + callback(stream.current(), style, i, stream.start, state, mode); stream.start = stream.pos; } } diff --git a/lib/redactor/codemirror/addon/runmode/runmode.node.js b/lib/redactor/codemirror/addon/runmode/runmode.node.js index 53b6994..9cac772 100644 --- a/lib/redactor/codemirror/addon/runmode/runmode.node.js +++ b/lib/redactor/codemirror/addon/runmode/runmode.node.js @@ -1,197 +1,329 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +'use strict'; -/* Just enough of CodeMirror to run runMode under node.js */ - -function splitLines(string){return string.split(/\r\n?|\n/);}; +function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (Object.prototype.hasOwnProperty.call(obj, prop) && (overwrite !== false || !Object.prototype.hasOwnProperty.call(target, prop))) + { target[prop] = obj[prop]; } } + return target +} // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. -var countColumn = exports.countColumn = function(string, end, tabSize, startIndex, startValue) { +function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); - if (end == -1) end = string.length; + if (end == -1) { end = string.length; } } for (var i = startIndex || 0, n = startValue || 0;;) { var nextTab = string.indexOf("\t", i); if (nextTab < 0 || nextTab >= end) - return n + (end - i); + { return n + (end - i) } n += nextTab - i; n += tabSize - (n % tabSize); i = nextTab + 1; } -}; +} -function StringStream(string, tabSize, context) { +function nothing() {} + +function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst +} + +// STRING STREAM + +// Fed to the mode parsers, provides helper functions to make +// parsers more succinct. + +var StringStream = function(string, tabSize, lineOracle) { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; this.lineStart = 0; - this.context = context + this.lineOracle = lineOracle; }; -StringStream.prototype = { - eol: function() {return this.pos >= this.string.length;}, - sol: function() {return this.pos == this.lineStart;}, - peek: function() {return this.string.charAt(this.pos) || undefined;}, - next: function() { - if (this.pos < this.string.length) - return this.string.charAt(this.pos++); - }, - eat: function(match) { - var ch = this.string.charAt(this.pos); - if (typeof match == "string") var ok = ch == match; - else var ok = ch && (match.test ? match.test(ch) : match(ch)); - if (ok) {++this.pos; return ch;} - }, - eatWhile: function(match) { - var start = this.pos; - while (this.eat(match)){} - return this.pos > start; - }, - eatSpace: function() { - var start = this.pos; - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; - return this.pos > start; - }, - skipToEnd: function() {this.pos = this.string.length;}, - skipTo: function(ch) { - var found = this.string.indexOf(ch, this.pos); - if (found > -1) {this.pos = found; return true;} - }, - backUp: function(n) {this.pos -= n;}, - column: function() { - if (this.lastColumnPos < this.start) { - this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); - this.lastColumnPos = this.start; +StringStream.prototype.eol = function () {return this.pos >= this.string.length}; +StringStream.prototype.sol = function () {return this.pos == this.lineStart}; +StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; +StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } +}; +StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} +}; +StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start +}; +StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start +}; +StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; +StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} +}; +StringStream.prototype.backUp = function (n) {this.pos -= n;}; +StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) +}; +StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) +}; +StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true } - return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); - }, - indentation: function() { - return countColumn(this.string, null, this.tabSize) - - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); - }, - match: function(pattern, consume, caseInsensitive) { - if (typeof pattern == "string") { - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; - var substr = this.string.substr(this.pos, pattern.length); - if (cased(substr) == cased(pattern)) { - if (consume !== false) this.pos += pattern.length; - return true; - } - } else { - var match = this.string.slice(this.pos).match(pattern); - if (match && match.index > 0) return null; - if (match && consume !== false) this.pos += match[0].length; - return match; - } - }, - current: function(){return this.string.slice(this.start, this.pos);}, - hideFirstChars: function(n, inner) { - this.lineStart += n; - try { return inner(); } - finally { this.lineStart -= n; } - }, - lookAhead: function(n) { - var line = this.context.line + n - return line >= this.context.lines.length ? null : this.context.lines[line] + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match } }; -exports.StringStream = StringStream; - -exports.startState = function(mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true; +StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; +StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } +}; +StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) +}; +StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) }; -var modes = exports.modes = {}, mimeModes = exports.mimeModes = {}; -exports.defineMode = function(name, mode) { +// Known modes, by name and by MIME +var modes = {}, mimeModes = {}; + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +function defineMode(name, mode) { if (arguments.length > 2) - mode.dependencies = Array.prototype.slice.call(arguments, 2); + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } modes[name] = mode; -}; -exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; +} -exports.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd();}}; -}); -exports.defineMIME("text/plain", "null"); +function defineMIME(mime, spec) { + mimeModes[mime] = spec; +} -exports.resolveMode = function(spec) { +// Given a MIME type, a {name, ...options} config object, or a name +// string, return a mode config object. +function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { - spec = mimeModes[spec.name]; + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") } - if (typeof spec == "string") return {name: spec}; - else return spec || {name: "null"}; -}; - -function copyObj(obj, target, overwrite) { - if (!target) target = {}; - for (var prop in obj) - if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) - target[prop] = obj[prop]; - return target; + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } } -// This can be used to attach properties to mode objects from -// outside the actual mode definition. -var modeExtensions = exports.modeExtensions = {}; -exports.extendMode = function(mode, properties) { - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); - copyObj(properties, exts); -}; - -exports.getMode = function(options, spec) { - var spec = exports.resolveMode(spec); +// Given a mode spec (anything that resolveMode accepts), find and +// initialize an actual mode object. +function getMode(options, spec) { + spec = resolveMode(spec); var mfactory = modes[spec.name]; - if (!mfactory) return exports.getMode(options, "text/plain"); + if (!mfactory) { return getMode(options, "text/plain") } var modeObj = mfactory(options, spec); if (modeExtensions.hasOwnProperty(spec.name)) { var exts = modeExtensions[spec.name]; for (var prop in exts) { - if (!exts.hasOwnProperty(prop)) continue; - if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop]; + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } modeObj[prop] = exts[prop]; } } modeObj.name = spec.name; - if (spec.helperType) modeObj.helperType = spec.helperType; - if (spec.modeProps) for (var prop in spec.modeProps) - modeObj[prop] = spec.modeProps[prop]; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } - return modeObj; -}; + return modeObj +} -exports.innerMode = function(mode, state) { +// This can be used to attach properties to mode objects from +// outside the actual mode definition. +var modeExtensions = {}; +function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); +} + +function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate +} + +// Given a mode and a state (for that mode), find the inner mode and +// state at the position that the state refers to. +function innerMode(mode, state) { var info; while (mode.innerMode) { info = mode.innerMode(state); - if (!info || info.mode == mode) break; + if (!info || info.mode == mode) { break } state = info.state; mode = info.mode; } - return info || {mode: mode, state: state}; + return info || {mode: mode, state: state} } -exports.registerHelper = exports.registerGlobalHelper = Math.min; +function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true +} -exports.runMode = function(string, modespec, callback, options) { - var mode = exports.getMode({indentUnit: 2}, modespec); - var lines = splitLines(string), state = (options && options.state) || exports.startState(mode); - var context = {lines: lines, line: 0} - for (var i = 0, e = lines.length; i < e; ++i, ++context.line) { - if (i) callback("\n"); - var stream = new exports.StringStream(lines[i], 4, context); - if (!stream.string && mode.blankLine) mode.blankLine(state); +var modeMethods = { + __proto__: null, + modes: modes, + mimeModes: mimeModes, + defineMode: defineMode, + defineMIME: defineMIME, + resolveMode: resolveMode, + getMode: getMode, + modeExtensions: modeExtensions, + extendMode: extendMode, + copyState: copyState, + innerMode: innerMode, + startState: startState +}; + +// Copy StringStream and mode methods into exports (CodeMirror) object. +exports.StringStream = StringStream; +exports.countColumn = countColumn; +for (var exported in modeMethods) { exports[exported] = modeMethods[exported]; } + +// Shim library CodeMirror with the minimal CodeMirror defined above. +require.cache[require.resolve("../../lib/codemirror")] = require.cache[require.resolve("./runmode.node")]; +require.cache[require.resolve("../../addon/runmode/runmode")] = require.cache[require.resolve("./runmode.node")]; + +// Minimal default mode. +exports.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); +exports.defineMIME("text/plain", "null"); + +exports.registerHelper = exports.registerGlobalHelper = Math.min; +exports.splitLines = function(string) { return string.split(/\r?\n|\r/) }; + +exports.defaults = { indentUnit: 2 }; + +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + { mod(require("../../lib/codemirror")); } + else if (typeof define == "function" && define.amd) // AMD + { define(["../../lib/codemirror"], mod); } + else // Plain browser env + { mod(CodeMirror); } +})(function(CodeMirror) { + +CodeMirror.runMode = function(string, modespec, callback, options) { + var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); + var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; + + // Create a tokenizing callback function if passed-in callback is a DOM element. + if (callback.appendChild) { + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); + var node = callback, col = 0; + node.textContent = ""; + callback = function(text, style) { + if (text == "\n") { + // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. + // Emitting a carriage return makes everything ok. + node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text)); + col = 0; + return; + } + var content = ""; + // replace tabs + for (var pos = 0;;) { + var idx = text.indexOf("\t", pos); + if (idx == -1) { + content += text.slice(pos); + col += text.length - pos; + break; + } else { + col += idx - pos; + content += text.slice(pos, idx); + var size = tabSize - col % tabSize; + col += size; + for (var i = 0; i < size; ++i) { content += " "; } + pos = idx + 1; + } + } + // Create a node with token style and append it to the callback DOM element. + if (style) { + var sp = node.appendChild(document.createElement("span")); + sp.className = "cm-" + style.replace(/ +/g, " cm-"); + sp.appendChild(document.createTextNode(content)); + } else { + node.appendChild(document.createTextNode(content)); + } + }; + } + + var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) { callback("\n"); } + var stream = new CodeMirror.StringStream(lines[i], null, { + lookAhead: function(n) { return lines[i + n] }, + baseToken: function() {} + }); + if (!stream.string && mode.blankLine) { mode.blankLine(state); } while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start, state); + callback(stream.current(), style, i, stream.start, state, mode); stream.start = stream.pos; } } }; -require.cache[require.resolve("../../lib/codemirror")] = require.cache[require.resolve("./runmode.node")]; -require.cache[require.resolve("../../addon/runmode/runmode")] = require.cache[require.resolve("./runmode.node")]; +}); diff --git a/lib/redactor/codemirror/addon/scroll/annotatescrollbar.js b/lib/redactor/codemirror/addon/scroll/annotatescrollbar.js index 9fe61ec..b8d52b7 100644 --- a/lib/redactor/codemirror/addon/scroll/annotatescrollbar.js +++ b/lib/redactor/codemirror/addon/scroll/annotatescrollbar.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -72,10 +72,16 @@ var wrapping = cm.getOption("lineWrapping"); var singleLineH = wrapping && cm.defaultTextHeight() * 1.5; var curLine = null, curLineObj = null; + function getY(pos, top) { if (curLine != pos.line) { - curLine = pos.line; - curLineObj = cm.getLineHandle(curLine); + curLine = pos.line + curLineObj = cm.getLineHandle(pos.line) + var visual = cm.getLineHandleVisualStart(curLineObj) + if (visual != curLineObj) { + curLine = cm.getLineNumber(visual) + curLineObj = visual + } } if ((curLineObj.widgets && curLineObj.widgets.length) || (wrapping && curLineObj.height > singleLineH)) diff --git a/lib/redactor/codemirror/addon/scroll/scrollpastend.js b/lib/redactor/codemirror/addon/scroll/scrollpastend.js index 2ed9d95..310fb62 100644 --- a/lib/redactor/codemirror/addon/scroll/scrollpastend.js +++ b/lib/redactor/codemirror/addon/scroll/scrollpastend.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/scroll/simplescrollbars.js b/lib/redactor/codemirror/addon/scroll/simplescrollbars.js index 750a2bd..f1356a8 100644 --- a/lib/redactor/codemirror/addon/scroll/simplescrollbars.js +++ b/lib/redactor/codemirror/addon/scroll/simplescrollbars.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/search/jump-to-line.js b/lib/redactor/codemirror/addon/search/jump-to-line.js index 1f3526d..596383b 100644 --- a/lib/redactor/codemirror/addon/search/jump-to-line.js +++ b/lib/redactor/codemirror/addon/search/jump-to-line.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Defines jumpToLine command. Uses dialog.js if present. @@ -13,8 +13,11 @@ })(function(CodeMirror) { "use strict"; + // default search panel location + CodeMirror.defineOption("search", {bottom: false}); + function dialog(cm, text, shortText, deflt, f) { - if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true}); + if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom}); else f(prompt(shortText, deflt)); } diff --git a/lib/redactor/codemirror/addon/search/match-highlighter.js b/lib/redactor/codemirror/addon/search/match-highlighter.js index b344ac7..f7e7fb3 100644 --- a/lib/redactor/codemirror/addon/search/match-highlighter.js +++ b/lib/redactor/codemirror/addon/search/match-highlighter.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Highlighting text that matches the selection // @@ -16,7 +16,7 @@ // highlighted only if the selected text is a word. showToken, when enabled, // will cause the current token to be highlighted when nothing is selected. // delay is used to specify how much time to wait, in milliseconds, before -// highlighting the matches. If annotateScrollbar is enabled, the occurences +// highlighting the matches. If annotateScrollbar is enabled, the occurrences // will be highlighted on the scrollbar via the matchesonscrollbar addon. (function(mod) { @@ -90,7 +90,9 @@ var state = cm.state.matchHighlighter; cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style)); if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) { - var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + "\\b") : query; + var searchFor = hasBoundary ? new RegExp((/\w/.test(query.charAt(0)) ? "\\b" : "") + + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + + (/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query; state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false, {className: "CodeMirror-selection-highlight-scrollbar"}); } diff --git a/lib/redactor/codemirror/addon/search/matchesonscrollbar.js b/lib/redactor/codemirror/addon/search/matchesonscrollbar.js index 8a4a827..acab47b 100644 --- a/lib/redactor/codemirror/addon/search/matchesonscrollbar.js +++ b/lib/redactor/codemirror/addon/search/matchesonscrollbar.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/search/search.js b/lib/redactor/codemirror/addon/search/search.js index cecdd52..9662b09 100644 --- a/lib/redactor/codemirror/addon/search/search.js +++ b/lib/redactor/codemirror/addon/search/search.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Define search commands. Depends on dialog.js or another // implementation of the openDialog method. @@ -19,6 +19,9 @@ })(function(CodeMirror) { "use strict"; + // default search panel location + CodeMirror.defineOption("search", {bottom: false}); + function searchOverlay(query, caseInsensitive) { if (typeof query == "string") query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g"); @@ -63,12 +66,13 @@ selectValueOnOpen: true, closeOnEnter: false, onClose: function() { clearSearch(cm); }, - onKeyDown: onKeyDown + onKeyDown: onKeyDown, + bottom: cm.options.search.bottom }); } function dialog(cm, text, shortText, deflt, f) { - if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true}); + if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom}); else f(prompt(shortText, deflt)); } @@ -185,18 +189,46 @@ if (state.annotate) { state.annotate.clear(); state.annotate = null; } });} + function el(tag, attrs) { + var element = tag ? document.createElement(tag) : document.createDocumentFragment(); + for (var key in attrs) { + element[key] = attrs[key]; + } + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i] + element.appendChild(typeof child == "string" ? document.createTextNode(child) : child); + } + return element; + } function getQueryDialog(cm) { - return '' + cm.phrase("Search:") + ' ' + cm.phrase("(Use /re/ syntax for regexp search)") + ''; + var label = el("label", {className: "CodeMirror-search-label"}, + cm.phrase("Search:"), + el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field", + id: "CodeMirror-search-field"})); + label.setAttribute("for","CodeMirror-search-field"); + return el("", null, label, " ", + el("span", {style: "color: #666", className: "CodeMirror-search-hint"}, + cm.phrase("(Use /re/ syntax for regexp search)"))); } function getReplaceQueryDialog(cm) { - return ' ' + cm.phrase("(Use /re/ syntax for regexp search)") + ''; + return el("", null, " ", + el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}), " ", + el("span", {style: "color: #666", className: "CodeMirror-search-hint"}, + cm.phrase("(Use /re/ syntax for regexp search)"))); } function getReplacementQueryDialog(cm) { - return '' + cm.phrase("With:") + ' '; + return el("", null, + el("span", {className: "CodeMirror-search-label"}, cm.phrase("With:")), " ", + el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"})); } function getDoReplaceConfirm(cm) { - return '' + cm.phrase("Replace?") + ' '; + return el("", null, + el("span", {className: "CodeMirror-search-label"}, cm.phrase("Replace?")), " ", + el("button", {}, cm.phrase("Yes")), " ", + el("button", {}, cm.phrase("No")), " ", + el("button", {}, cm.phrase("All")), " ", + el("button", {}, cm.phrase("Stop"))); } function replaceAll(cm, query, text) { @@ -213,8 +245,11 @@ function replace(cm, all) { if (cm.getOption("readOnly")) return; var query = cm.getSelection() || getSearchState(cm).lastQuery; - var dialogText = '' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + ''; - dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) { + var dialogText = all ? cm.phrase("Replace all:") : cm.phrase("Replace:") + var fragment = el("", null, + el("span", {className: "CodeMirror-search-label"}, dialogText), + getReplaceQueryDialog(cm)) + dialog(cm, fragment, dialogText, query, function(query) { if (!query) return; query = parseQuery(query); dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) { diff --git a/lib/redactor/codemirror/addon/search/searchcursor.js b/lib/redactor/codemirror/addon/search/searchcursor.js index 816bf77..ffe6932 100644 --- a/lib/redactor/codemirror/addon/search/searchcursor.js +++ b/lib/redactor/codemirror/addon/search/searchcursor.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -202,6 +202,7 @@ function SearchCursor(doc, query, pos, options) { this.atOccurrence = false + this.afterEmptyMatch = false this.doc = doc pos = pos ? doc.clipPos(pos) : Pos(0, 0) this.pos = {from: pos, to: pos} @@ -237,21 +238,29 @@ findPrevious: function() {return this.find(true)}, find: function(reverse) { - var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to)) - - // Implements weird auto-growing behavior on null-matches for - // backwards-compatiblity with the vim code (unfortunately) - while (result && CodeMirror.cmpPos(result.from, result.to) == 0) { + var head = this.doc.clipPos(reverse ? this.pos.from : this.pos.to); + if (this.afterEmptyMatch && this.atOccurrence) { + // do not return the same 0 width match twice + head = Pos(head.line, head.ch) if (reverse) { - if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1) - else if (result.from.line == this.doc.firstLine()) result = null - else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1))) + head.ch--; + if (head.ch < 0) { + head.line--; + head.ch = (this.doc.getLine(head.line) || "").length; + } } else { - if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1) - else if (result.to.line == this.doc.lastLine()) result = null - else result = this.matches(reverse, Pos(result.to.line + 1, 0)) + head.ch++; + if (head.ch > (this.doc.getLine(head.line) || "").length) { + head.ch = 0; + head.line++; + } + } + if (CodeMirror.cmpPos(head, this.doc.clipPos(head)) != 0) { + return this.atOccurrence = false } } + var result = this.matches(reverse, head) + this.afterEmptyMatch = result && CodeMirror.cmpPos(result.from, result.to) == 0 if (result) { this.pos = result diff --git a/lib/redactor/codemirror/addon/selection/active-line.js b/lib/redactor/codemirror/addon/selection/active-line.js index c7b14ce..ff81ffc 100644 --- a/lib/redactor/codemirror/addon/selection/active-line.js +++ b/lib/redactor/codemirror/addon/selection/active-line.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/selection/mark-selection.js b/lib/redactor/codemirror/addon/selection/mark-selection.js index adfaa62..fc80159 100644 --- a/lib/redactor/codemirror/addon/selection/mark-selection.js +++ b/lib/redactor/codemirror/addon/selection/mark-selection.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Because sometimes you need to mark the selected *text*. // diff --git a/lib/redactor/codemirror/addon/selection/selection-pointer.js b/lib/redactor/codemirror/addon/selection/selection-pointer.js index f0bd61a..924b465 100644 --- a/lib/redactor/codemirror/addon/selection/selection-pointer.js +++ b/lib/redactor/codemirror/addon/selection/selection-pointer.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/addon/tern/tern.js b/lib/redactor/codemirror/addon/tern/tern.js index 253309d..8a4d67d 100644 --- a/lib/redactor/codemirror/addon/tern/tern.js +++ b/lib/redactor/codemirror/addon/tern/tern.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // Glue code between CodeMirror and Tern. // @@ -231,8 +231,7 @@ var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc; if (content) { tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset, - node.getBoundingClientRect().top + window.pageYOffset, content); - tooltip.className += " " + cls + "hint-doc"; + node.getBoundingClientRect().top + window.pageYOffset, content, cm, cls + "hint-doc"); } }); c(obj); @@ -334,7 +333,7 @@ tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")")); if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype)); var place = cm.cursorCoords(null, "page"); - var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip) + var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip, cm) setTimeout(function() { tooltip.clear = onEditorActivity(cm, function() { if (ts.activeArgHints == tooltip) closeArgHints(ts) }) @@ -590,10 +589,16 @@ } function dialog(cm, text, f) { - if (cm.openDialog) - cm.openDialog(text + ": ", f); - else + if (cm.openDialog) { + var fragment = document.createDocumentFragment(); + fragment.appendChild(document.createTextNode(text + ": ")); + var input = document.createElement("input"); + input.type = "text"; + fragment.appendChild(input); + cm.openDialog(fragment, f); + } else { f(prompt(text, "")); + } } // Tooltips @@ -601,7 +606,7 @@ function tempTooltip(cm, content, ts) { if (cm.state.ternTooltip) remove(cm.state.ternTooltip); var where = cm.cursorCoords(); - var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content); + var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content, cm); function maybeClear() { old = true; if (!mouseOnTip) clear(); @@ -637,11 +642,44 @@ } } - function makeTooltip(x, y, content) { - var node = elt("div", cls + "tooltip", content); + function makeTooltip(x, y, content, cm, className) { + var node = elt("div", cls + "tooltip" + " " + (className || ""), content); node.style.left = x + "px"; node.style.top = y + "px"; - document.body.appendChild(node); + var container = ((cm.options || {}).hintOptions || {}).container || document.body; + container.appendChild(node); + + var pos = cm.cursorCoords(); + var winW = window.innerWidth; + var winH = window.innerHeight; + var box = node.getBoundingClientRect(); + var hints = document.querySelector(".CodeMirror-hints"); + var overlapY = box.bottom - winH; + var overlapX = box.right - winW; + + if (hints && overlapX > 0) { + node.style.left = 0; + var box = node.getBoundingClientRect(); + node.style.left = (x = x - hints.offsetWidth - box.width) + "px"; + overlapX = box.right - winW; + } + if (overlapY > 0) { + var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); + if (curTop - height > 0) { // Fits above cursor + node.style.top = (pos.top - height) + "px"; + } else if (height > winH) { + node.style.height = (winH - 5) + "px"; + node.style.top = (pos.bottom - box.top) + "px"; + } + } + if (overlapX > 0) { + if (box.right - box.left > winW) { + node.style.width = (winW - 5) + "px"; + overlapX -= (box.right - box.left) - winW; + } + node.style.left = (x - overlapX) + "px"; + } + return node; } diff --git a/lib/redactor/codemirror/addon/tern/worker.js b/lib/redactor/codemirror/addon/tern/worker.js index e134ad4..ed1b685 100644 --- a/lib/redactor/codemirror/addon/tern/worker.js +++ b/lib/redactor/codemirror/addon/tern/worker.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE // declare global: tern, server diff --git a/lib/redactor/codemirror/addon/wrap/hardwrap.js b/lib/redactor/codemirror/addon/wrap/hardwrap.js index 29cc15f..e7053b5 100644 --- a/lib/redactor/codemirror/addon/wrap/hardwrap.js +++ b/lib/redactor/codemirror/addon/wrap/hardwrap.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -29,11 +29,20 @@ return {from: start, to: end}; } - function findBreakPoint(text, column, wrapOn, killTrailingSpace) { + function findBreakPoint(text, column, wrapOn, killTrailingSpace, forceBreak) { var at = column while (at < text.length && text.charAt(at) == " ") at++ for (; at > 0; --at) if (wrapOn.test(text.slice(at - 1, at + 1))) break; + + if (!forceBreak && at <= text.match(/^[ \t]*/)[0].length) { + // didn't find a break point before column, in non-forceBreak mode try to + // find one after 'column'. + for (at = column + 1; at < text.length - 1; ++at) { + if (wrapOn.test(text.slice(at - 1, at + 1))) break; + } + } + for (var first = true;; first = false) { var endOfText = at; if (killTrailingSpace) @@ -47,6 +56,7 @@ from = cm.clipPos(from); to = cm.clipPos(to); var column = options.column || 80; var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/; + var forceBreak = options.forceBreak !== false; var killTrailing = options.killTrailingSpace !== false; var changes = [], curLine = "", curNo = from.line; var lines = cm.getRange(from, to, false); @@ -68,7 +78,7 @@ curLine += text; if (i) { var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed && - findBreakPoint(curLine, column, wrapOn, killTrailing); + findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak); // If this isn't broken, or is broken at a different point, remove old break if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) { changes.push({text: [spaceInserted ? " " : ""], @@ -80,12 +90,17 @@ } } while (curLine.length > column) { - var bp = findBreakPoint(curLine, column, wrapOn, killTrailing); - changes.push({text: ["", leadingSpace], - from: Pos(curNo, bp.from), - to: Pos(curNo, bp.to)}); - curLine = leadingSpace + curLine.slice(bp.to); - ++curNo; + var bp = findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak); + if (bp.from != bp.to || + forceBreak && leadingSpace !== curLine.slice(0, bp.to)) { + changes.push({text: ["", leadingSpace], + from: Pos(curNo, bp.from), + to: Pos(curNo, bp.to)}); + curLine = leadingSpace + curLine.slice(bp.to); + ++curNo; + } else { + break; + } } } if (changes.length) cm.operation(function() { diff --git a/lib/redactor/codemirror/lib/codemirror.css b/lib/redactor/codemirror/lib/codemirror.css index 98d9fe8..f4d5718 100644 --- a/lib/redactor/codemirror/lib/codemirror.css +++ b/lib/redactor/codemirror/lib/codemirror.css @@ -6,7 +6,6 @@ height: 300px; color: black; direction: ltr; - font-size: 15px !important; } /* PADDING */ @@ -61,20 +60,13 @@ .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } -.cm-fat-cursor-mark { - background-color: rgba(20, 255, 20, 0.5); - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; -} -.cm-animate-fat-cursor { - width: auto; - border: 0; - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; - background-color: #7e7; -} +.cm-fat-cursor .CodeMirror-line::selection, +.cm-fat-cursor .CodeMirror-line > span::selection, +.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; } +.cm-fat-cursor .CodeMirror-line::-moz-selection, +.cm-fat-cursor .CodeMirror-line > span::-moz-selection, +.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; } +.cm-fat-cursor { caret-color: transparent; } @-moz-keyframes blink { 0% {} 50% { background-color: transparent; } @@ -165,17 +157,18 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ - /* 30px is the magic margin used to hide the element's real scrollbars */ + /* 50px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ - margin-bottom: -30px; margin-right: -30px; - padding-bottom: 30px; + margin-bottom: -50px; margin-right: -50px; + padding-bottom: 50px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; + z-index: 0; } .CodeMirror-sizer { position: relative; - border-right: 30px solid transparent; + border-right: 50px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling @@ -185,6 +178,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} position: absolute; z-index: 6; display: none; + outline: none; } .CodeMirror-vscrollbar { right: 0; top: 0; @@ -213,7 +207,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} height: 100%; display: inline-block; vertical-align: top; - margin-bottom: -30px; + margin-bottom: -50px; } .CodeMirror-gutter-wrapper { position: absolute; diff --git a/lib/redactor/codemirror/lib/codemirror.js b/lib/redactor/codemirror/lib/codemirror.js index 72267f6..df0b5ff 100644 --- a/lib/redactor/codemirror/lib/codemirror.js +++ b/lib/redactor/codemirror/lib/codemirror.js @@ -1,7 +1,7 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE -// This is CodeMirror (https://codemirror.net), a code editor +// This is CodeMirror (https://codemirror.net/5), 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 @@ -10,7 +10,7 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global.CodeMirror = factory()); + (global = global || self, global.CodeMirror = factory()); }(this, (function () { 'use strict'; // Kludges for bugs and behavior differences that can't be feature @@ -26,13 +26,14 @@ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); var webkit = !edge && /WebKit\//.test(userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); - var chrome = !edge && /Chrome\//.test(userAgent); + var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent); + var chrome_version = chrome && +chrome[1]; var presto = /Opera\//.test(userAgent); var safari = /Apple Computer/.test(navigator.vendor); var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); - var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); var android = /Android/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); @@ -111,15 +112,16 @@ } while (child = child.parentNode) } - function activeElt() { + function activeElt(rootNode) { // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. // IE < 10 will throw when accessed while the page is loading or in an iframe. // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var doc = rootNode.ownerDocument || rootNode; var activeElement; try { - activeElement = document.activeElement; + activeElement = rootNode.activeElement; } catch(e) { - activeElement = document.body || null; + activeElement = doc.body || null; } while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) { activeElement = activeElement.shadowRoot.activeElement; } @@ -143,6 +145,19 @@ else if (ie) // Suppress mysterious IE10 errors { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + function doc(cm) { return cm.display.wrapper.ownerDocument } + + function root(cm) { + return rootNode(cm.display.wrapper) + } + + function rootNode(element) { + // Detect modern browsers (2017+). + return element.getRootNode ? element.getRootNode() : element.ownerDocument + } + + function win(cm) { return doc(cm).defaultView } + function bind(f) { var args = Array.prototype.slice.call(arguments, 1); return function(){return f.apply(null, args)} @@ -151,7 +166,7 @@ function copyObj(obj, target, overwrite) { if (!target) { target = {}; } for (var prop in obj) - { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { if (Object.prototype.hasOwnProperty.call(obj, prop) && (overwrite !== false || !Object.prototype.hasOwnProperty.call(target, prop))) { target[prop] = obj[prop]; } } return target } @@ -204,7 +219,7 @@ } // Number of pixels added to scroller and sizer to hide scrollbar - var scrollerGap = 30; + var scrollerGap = 50; // Returned or thrown by various protocols to signal 'I'm not // handling this'. @@ -485,14 +500,15 @@ for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} order.push(new BidiSpan(0, start, i$7)); } else { - var pos = i$7, at = order.length; + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (var j$2 = pos; j$2 < i$7;) { if (countsAsNum.test(types[j$2])) { - if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } var nstart = j$2; for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; pos = j$2; } else { ++j$2; } } @@ -536,8 +552,8 @@ } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f); } else { - var map$$1 = emitter._handlers || (emitter._handlers = {}); - map$$1[type] = (map$$1[type] || noHandlers).concat(f); + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); } }; @@ -551,11 +567,11 @@ } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f); } else { - var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; + var map = emitter._handlers, arr = map && map[type]; if (arr) { var index = indexOf(arr, f); if (index > -1) - { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } @@ -683,11 +699,11 @@ try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function (te) { - var range$$1; - try {range$$1 = te.ownerDocument.selection.createRange();} + var range; + try {range = te.ownerDocument.selection.createRange();} catch(e) {} - if (!range$$1 || range$$1.parentElement() != te) { return false } - return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 }; var hasCopyEvent = (function () { @@ -835,10 +851,8 @@ return this.pos > start }; StringStream.prototype.eatSpace = function () { - var this$1 = this; - var start = this.pos; - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } return this.pos > start }; StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; @@ -1034,11 +1048,9 @@ }; Context.prototype.baseToken = function (n) { - var this$1 = this; - if (!this.baseTokens) { return null } while (this.baseTokens[this.baseTokenPos] <= n) - { this$1.baseTokenPos += 2; } + { this.baseTokenPos += 2; } var type = this.baseTokens[this.baseTokenPos + 1]; return {type: type && type.replace(/( |^)overlay .*/, ""), size: this.baseTokens[this.baseTokenPos] - n} @@ -1204,7 +1216,7 @@ var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) { output[prop] = lineClass[2]; } - else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) { output[prop] += " " + lineClass[2]; } } } return type @@ -1314,6 +1326,7 @@ if (span.marker == marker) { return span } } } } + // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). function removeMarkedSpan(spans, span) { @@ -1322,9 +1335,16 @@ { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } return r } + // Add a span to a line. - function addMarkedSpan(line, span) { - line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } span.marker.attachLine(line); } @@ -1527,8 +1547,8 @@ // 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. - function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { - var line = getLine(doc, lineNo$$1); + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; @@ -1844,7 +1864,7 @@ } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; - if (style || startStyle || endStyle || mustWrap || css) { + if (style || startStyle || endStyle || mustWrap || css || attributes) { var fullStyle = style || ""; if (startStyle) { fullStyle += startStyle; } if (endStyle) { fullStyle += endStyle; } @@ -2189,6 +2209,7 @@ if (cm.options.lineNumbers || markers) { var wrap$1 = ensureLineWrapped(lineView); var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); cm.display.input.setUneditable(gutterWrap); wrap$1.insertBefore(gutterWrap, lineView.text); if (lineView.line.gutterClass) @@ -2345,12 +2366,14 @@ function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) { return {map: lineView.measure.map, cache: lineView.measure.cache} } - for (var i = 0; i < lineView.rest.length; i++) - { if (lineView.rest[i] == line) - { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } - for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) - { if (lineNo(lineView.rest[i$1]) > lineN) - { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + if (lineView.rest) { + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } } // Render a line into the hidden node display.externalMeasured. Used @@ -2430,36 +2453,36 @@ var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; - function nodeAndOffsetInLineMap(map$$1, ch, bias) { + function nodeAndOffsetInLineMap(map, ch, bias) { var node, start, end, collapse, mStart, mEnd; // First, search the line map for the text node corresponding to, // or closest to, the target character. - for (var i = 0; i < map$$1.length; i += 3) { - mStart = map$$1[i]; - mEnd = map$$1[i + 1]; + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; - } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) { collapse = "right"; } } if (start != null) { - node = map$$1[i + 2]; + node = map[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) { collapse = bias; } if (bias == "left" && start == 0) - { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { - node = map$$1[(i -= 3) + 2]; + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; collapse = "left"; } } if (bias == "right" && start == mEnd - mStart) - { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { - node = map$$1[(i += 3) + 2]; + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; collapse = "right"; } } break @@ -2564,22 +2587,24 @@ cm.display.lineNumChars = null; } - function pageScrollX() { + function pageScrollX(doc) { // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 // which causes page_Offset and bounding client rects to use // different reference viewports and invalidate our calculations. - if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } - return window.pageXOffset || (document.documentElement || document.body).scrollLeft + if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) } + return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft } - function pageScrollY() { - if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } - return window.pageYOffset || (document.documentElement || document.body).scrollTop + function pageScrollY(doc) { + if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) } + return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop } function widgetTopHeight(lineObj) { + var ref = visualLine(lineObj); + var widgets = ref.widgets; var height = 0; - if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) - { height += widgetHeight(lineObj.widgets[i]); } } } + if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above) + { height += widgetHeight(widgets[i]); } } } return height } @@ -2599,8 +2624,8 @@ else { yOff -= cm.display.viewOffset; } if (context == "page" || context == "window") { var lOff = cm.display.lineSpace.getBoundingClientRect(); - yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); - var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm))); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm))); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; @@ -2614,8 +2639,8 @@ var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { - left -= pageScrollX(); - top -= pageScrollY(); + left -= pageScrollX(doc(cm)); + top -= pageScrollY(doc(cm)); } else if (context == "local" || !context) { var localBox = cm.display.sizer.getBoundingClientRect(); left += localBox.left; @@ -2742,13 +2767,13 @@ return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x } - function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { + function coordsCharInner(cm, lineObj, lineNo, x, y) { // Move y into line-local coordinate space y -= heightAtLine(lineObj); var preparedMeasure = prepareMeasureForLine(cm, lineObj); // When directly calling `measureCharPrepared`, we have to adjust // for the widgets at this line. - var widgetHeight$$1 = widgetTopHeight(lineObj); + var widgetHeight = widgetTopHeight(lineObj); var begin = 0, end = lineObj.text.length, ltr = true; var order = getOrder(lineObj, cm.doc.direction); @@ -2756,7 +2781,7 @@ // which bidi section the coordinates fall into. if (order) { var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) - (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y); + (cm, lineObj, lineNo, preparedMeasure, order, x, y); ltr = part.level != 1; // The awkward -1 offsets are needed because findFirst (called // on these below) will treat its first bound as inclusive, @@ -2772,7 +2797,7 @@ var chAround = null, boxAround = null; var ch = findFirst(function (ch) { var box = measureCharPrepared(cm, preparedMeasure, ch); - box.top += widgetHeight$$1; box.bottom += widgetHeight$$1; + box.top += widgetHeight; box.bottom += widgetHeight; if (!boxIsAfter(box, x, y, false)) { return false } if (box.top <= y && box.left <= x) { chAround = ch; @@ -2796,27 +2821,27 @@ // left of the character and compare it's vertical position to the // coordinates sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : - (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ? + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? "after" : "before"; // Now get accurate coordinates for this place, in order to get a // base X position - var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure); + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); baseX = coords.left; outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; } ch = skipExtendingChars(lineObj.text, ch, 1); - return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX) + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) } - function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) { + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { // Bidi parts are sorted left-to-right, and in a non-line-wrapping // situation, we can take this ordering to correspond to the visual // ordering. This finds the first part whose end is after the given // coordinates. var index = findFirst(function (i) { var part = order[i], ltr = part.level != 1; - return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"), + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), "line", lineObj, preparedMeasure), x, y, true) }, 0, order.length - 1); var part = order[index]; @@ -2825,7 +2850,7 @@ // that start, move one part back. if (index > 0) { var ltr = part.level != 1; - var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"), + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), "line", lineObj, preparedMeasure); if (boxIsAfter(start, x, y, true) && start.top > y) { part = order[index - 1]; } @@ -2963,7 +2988,7 @@ var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } - catch (e) { return null } + catch (e$1) { return null } var coords = coordsChar(cm, x, y), line; if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; @@ -3144,15 +3169,21 @@ var curFragment = result.cursors = document.createDocumentFragment(); var selFragment = result.selection = document.createDocumentFragment(); + var customCursor = cm.options.$customCursor; + if (customCursor) { primary = true; } for (var i = 0; i < doc.sel.ranges.length; i++) { if (!primary && i == doc.sel.primIndex) { continue } - var range$$1 = doc.sel.ranges[i]; - if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } - var collapsed = range$$1.empty(); - if (collapsed || cm.options.showCursorWhenSelecting) - { drawSelectionCursor(cm, range$$1.head, curFragment); } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (customCursor) { + var head = customCursor(cm, range); + if (head) { drawSelectionCursor(cm, head, curFragment); } + } else if (collapsed || cm.options.showCursorWhenSelecting) { + drawSelectionCursor(cm, range.head, curFragment); + } if (!collapsed) - { drawSelectionRange(cm, range$$1, selFragment); } + { drawSelectionRange(cm, range, selFragment); } } return result } @@ -3166,6 +3197,12 @@ cursor.style.top = pos.top + "px"; cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) { + var charPos = charCoords(cm, head, "div", null, null); + var width = charPos.right - charPos.left; + cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"; + } + if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); @@ -3179,7 +3216,7 @@ function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } // Draws the given range as a highlighted selection - function drawSelectionRange(cm, range$$1, output) { + function drawSelectionRange(cm, range, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; @@ -3248,7 +3285,7 @@ return {start: start, end: end} } - var sFrom = range$$1.from(), sTo = range$$1.to(); + var sFrom = range.from(), sTo = range.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { @@ -3279,26 +3316,31 @@ var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) - { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, - cm.options.cursorBlinkRate); } + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } else if (cm.options.cursorBlinkRate < 0) { display.cursorDiv.style.visibility = "hidden"; } } function ensureFocus(cm) { - if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } } function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function () { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; - onBlur(cm); + if (cm.state.focused) { onBlur(cm); } } }, 100); } function onFocus(cm, e) { - if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } if (cm.options.readOnly == "nocursor") { return } if (!cm.state.focused) { @@ -3333,10 +3375,14 @@ function updateHeightsInViewport(cm) { var display = cm.display; var prevBottom = display.lineDiv.offsetTop; + var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top); + var oldHeight = display.lineDiv.getBoundingClientRect().top; + var mustScroll = 0; for (var i = 0; i < display.view.length; i++) { var cur = display.view[i], wrapping = cm.options.lineWrapping; var height = (void 0), width = 0; if (cur.hidden) { continue } + oldHeight += cur.line.height; if (ie && ie_version < 8) { var bot = cur.node.offsetTop + cur.node.offsetHeight; height = bot - prevBottom; @@ -3351,6 +3397,7 @@ } var diff = cur.line.height - height; if (diff > .005 || diff < -.005) { + if (oldHeight < viewTop) { mustScroll -= diff; } updateLineHeight(cur.line, height); updateWidgetHeight(cur.line); if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) @@ -3365,6 +3412,7 @@ } } } + if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; } } // Read and store the height of line widgets associated with the @@ -3377,7 +3425,7 @@ } // Compute the lines that are visible in a given viewport (defaults - // the the current scroll position). viewport may contain top, + // the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewport) { var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; @@ -3408,8 +3456,9 @@ if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + var doc = display.wrapper.ownerDocument; if (rect.top + box.top < 0) { doScroll = true; } - else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; } if (doScroll != null && !phantom) { var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); cm.display.lineSpace.appendChild(scrollNode); @@ -3428,8 +3477,8 @@ // Set pos and end to the cursor positions around the character pos sticks to // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch // If pos == Pos(_, 0, "before"), pos and end are unchanged - pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; } for (var limit = 0; limit < 5; limit++) { var changed = false; @@ -3480,14 +3529,15 @@ if (newTop != screentop) { result.scrollTop = newTop; } } - var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; - var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; var tooWide = rect.right - rect.left > screenw; if (tooWide) { rect.right = rect.left + screenw; } if (rect.left < 10) { result.scrollLeft = 0; } else if (rect.left < screenleft) - { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } else if (rect.right > screenw + screenleft - 3) { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } return result @@ -3515,9 +3565,9 @@ if (y != null) { cm.curOp.scrollTop = y; } } - function scrollToRange(cm, range$$1) { + function scrollToRange(cm, range) { resolveScrollToPos(cm); - cm.curOp.scrollToPos = range$$1; + cm.curOp.scrollToPos = range; } // When an operation has its scrollToPos property set, and another @@ -3525,11 +3575,11 @@ // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { - var range$$1 = cm.curOp.scrollToPos; - if (range$$1) { + var range = cm.curOp.scrollToPos; + if (range) { cm.curOp.scrollToPos = null; - var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); - scrollToCoordsRange(cm, from, to, range$$1.margin); + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); } } @@ -3554,7 +3604,7 @@ } function setScrollTop(cm, val, forceScroll) { - val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); if (cm.display.scroller.scrollTop == val && !forceScroll) { return } cm.doc.scrollTop = val; cm.display.scrollbars.setScrollTop(val); @@ -3564,7 +3614,7 @@ // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller, forceScroll) { - val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } cm.doc.scrollLeft = val; alignHorizontally(cm); @@ -3624,6 +3674,7 @@ this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; } else { + this.vert.scrollTop = 0; this.vert.style.display = ""; this.vert.firstChild.style.height = "0"; } @@ -3661,13 +3712,13 @@ NativeScrollbars.prototype.zeroWidthHack = function () { var w = mac && !mac_geMountainLion ? "12px" : "18px"; this.horiz.style.height = this.vert.style.width = w; - this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.horiz.style.visibility = this.vert.style.visibility = "hidden"; this.disableHoriz = new Delayed; this.disableVert = new Delayed; }; NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { - bar.style.pointerEvents = "auto"; + bar.style.visibility = ""; function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom @@ -3676,9 +3727,9 @@ // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); - var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); - if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } + if (elt != bar) { bar.style.visibility = "hidden"; } else { delay.set(1000, maybeDisable); } } delay.set(1000, maybeDisable); @@ -3779,7 +3830,8 @@ scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollToPos: null, // Used to scroll to a specific position focus: false, - id: ++nextOpId // Unique ID + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan }; pushOperation(cm.curOp); } @@ -3858,7 +3910,7 @@ cm.display.maxLineChanged = false; } - var takeFocus = op.focus && op.focus == activeElt(); + var takeFocus = op.focus && op.focus == activeElt(root(cm)); if (op.preparedSelection) { cm.display.input.showSelection(op.preparedSelection, takeFocus); } if (op.updatedDisplay || op.startHeight != cm.doc.height) @@ -4018,10 +4070,8 @@ { this.events.push(arguments); } }; DisplayUpdate.prototype.finish = function () { - var this$1 = this; - for (var i = 0; i < this.events.length; i++) - { signal.apply(null, this$1.events[i]); } + { signal.apply(null, this.events[i]); } }; function maybeClipScrollbars(cm) { @@ -4037,11 +4087,11 @@ function selectionSnapshot(cm) { if (cm.hasFocus()) { return null } - var active = activeElt(); + var active = activeElt(root(cm)); if (!active || !contains(cm.display.lineDiv, active)) { return null } var result = {activeElt: active}; if (window.getSelection) { - var sel = window.getSelection(); + var sel = win(cm).getSelection(); if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { result.anchorNode = sel.anchorNode; result.anchorOffset = sel.anchorOffset; @@ -4053,14 +4103,16 @@ } function restoreSelection(snapshot) { - if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(rootNode(snapshot.activeElt))) { return } snapshot.activeElt.focus(); - if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { - var sel = window.getSelection(), range$$1 = document.createRange(); - range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); - range$$1.collapse(false); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var doc = snapshot.activeElt.ownerDocument; + var sel = doc.defaultView.getSelection(), range = doc.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); sel.removeAllRanges(); - sel.addRange(range$$1); + sel.addRange(range); sel.extend(snapshot.focusNode, snapshot.focusOffset); } } @@ -4153,6 +4205,8 @@ update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); } if (!updateDisplayIfNeeded(cm, update)) { break } updateHeightsInViewport(cm); @@ -4231,6 +4285,8 @@ function updateGutterSpace(display) { var width = display.gutters.offsetWidth; display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); } function setDocumentHeight(cm, measure) { @@ -4369,6 +4425,12 @@ d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + // See #6982. FIXME remove when this has been fixed for a while in Chrome + if (chrome && chrome_version === 105) { d.wrapper.style.clipPath = "inset(0px)"; } + + // This attribute is respected by automatic translation systems such as Google Translate, + // and may also be respected by tools used by human translators. + d.wrapper.setAttribute('translate', 'no'); // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } @@ -4466,7 +4528,24 @@ } function onScrollWheel(cm, e) { + // On Chrome 102, viewport updates somehow stop wheel-based + // scrolling. Turning off pointer events during the scroll seems + // to avoid the issue. + if (chrome && chrome_version == 102) { + if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; } + else { clearTimeout(cm.display.chromeScrollHack); } + cm.display.chromeScrollHack = setTimeout(function () { + cm.display.chromeScrollHack = null; + cm.display.sizer.style.pointerEvents = ""; + }, 100); + } var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var pixelsPerUnit = wheelPixelsPerUnit; + if (e.deltaMode === 0) { + dx = e.deltaX; + dy = e.deltaY; + pixelsPerUnit = 1; + } var display = cm.display, scroll = display.scroller; // Quit if there's nothing to scroll here @@ -4495,10 +4574,10 @@ // estimated pixels/delta value, we just handle horizontal // scrolling entirely here. It'll be slightly off from native, but // better than glitching out. - if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dx && !gecko && !presto && pixelsPerUnit != null) { if (dy && canScrollY) - { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } - setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit)); // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY @@ -4511,15 +4590,15 @@ // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). - if (dy && wheelPixelsPerUnit != null) { - var pixels = dy * wheelPixelsPerUnit; + if (dy && pixelsPerUnit != null) { + var pixels = dy * pixelsPerUnit; var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) { top = Math.max(0, top + pixels - 50); } else { bot = Math.min(cm.doc.height, bot + pixels + 50); } updateDisplaySimple(cm, {top: top, bottom: bot}); } - if (wheelSamples < 20) { + if (wheelSamples < 20 && e.deltaMode !== 0) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; display.wheelDX = dx; display.wheelDY = dy; @@ -4553,40 +4632,32 @@ Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; Selection.prototype.equals = function (other) { - var this$1 = this; - if (other == this) { return true } if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } for (var i = 0; i < this.ranges.length; i++) { - var here = this$1.ranges[i], there = other.ranges[i]; + var here = this.ranges[i], there = other.ranges[i]; if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } } return true }; Selection.prototype.deepCopy = function () { - var this$1 = this; - var out = []; for (var i = 0; i < this.ranges.length; i++) - { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } return new Selection(out, this.primIndex) }; Selection.prototype.somethingSelected = function () { - var this$1 = this; - for (var i = 0; i < this.ranges.length; i++) - { if (!this$1.ranges[i].empty()) { return true } } + { if (!this.ranges[i].empty()) { return true } } return false }; Selection.prototype.contains = function (pos, end) { - var this$1 = this; - if (!end) { end = pos; } for (var i = 0; i < this.ranges.length; i++) { - var range = this$1.ranges[i]; + var range = this.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) { return i } } @@ -4712,16 +4783,16 @@ } // Perform a change on the document data structure. - function updateDoc(doc, change, markedSpans, estimateHeight$$1) { + function updateDoc(doc, change, markedSpans, estimateHeight) { function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { - updateLine(line, text, spans, estimateHeight$$1); + updateLine(line, text, spans, estimateHeight); signalLater(line, "change", line, change); } function linesFor(start, end) { var result = []; for (var i = start; i < end; ++i) - { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } return result } @@ -4745,7 +4816,7 @@ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added$1 = linesFor(1, text.length - 1); - added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added$1); } @@ -4786,6 +4857,7 @@ estimateLineHeights(cm); loadMode(cm); setDirectionClass(cm); + cm.options.direction = doc.direction; if (!cm.options.lineWrapping) { findMaxLine(cm); } cm.options.mode = doc.modeOption; regChange(cm); @@ -4802,19 +4874,19 @@ }); } - function History(startGen) { + function History(prev) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. this.done = []; this.undone = []; - this.undoDepth = Infinity; + this.undoDepth = prev ? prev.undoDepth : Infinity; // Used to track when changes can be merged into a single undo // event this.lastModTime = this.lastSelTime = 0; this.lastOp = this.lastSelOp = null; this.lastOrigin = this.lastSelOrigin = null; // Used by the isClean() method - this.generation = this.maxGeneration = startGen || 1; + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; } // Create a history change event from an updateDoc-style change @@ -5082,11 +5154,9 @@ var obj = { ranges: sel.ranges, update: function(ranges) { - var this$1 = this; - this.ranges = []; for (var i = 0; i < ranges.length; i++) - { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); } }, origin: options && options.origin @@ -5121,7 +5191,7 @@ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); - if (!(options && options.scroll === false) && doc.cm) + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") { ensureCursorVisible(doc.cm); } } @@ -5152,7 +5222,7 @@ var range = sel.ranges[i]; var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); - var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear); if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) { out = sel.ranges.slice(0, i); } out[i] = new Range(newAnchor, newHead); @@ -5573,13 +5643,11 @@ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { - var this$1 = this; - this.lines = lines; this.parent = null; var height = 0; for (var i = 0; i < lines.length; ++i) { - lines[i].parent = this$1; + lines[i].parent = this; height += lines[i].height; } this.height = height; @@ -5590,11 +5658,9 @@ // Remove the n lines at offset 'at'. removeInner: function(at, n) { - var this$1 = this; - for (var i = at, e = at + n; i < e; ++i) { - var line = this$1.lines[i]; - this$1.height -= line.height; + var line = this.lines[i]; + this.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } @@ -5609,31 +5675,25 @@ // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function(at, lines, height) { - var this$1 = this; - this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); - for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { - var this$1 = this; - for (var e = at + n; at < e; ++at) - { if (op(this$1.lines[at])) { return true } } + { if (op(this.lines[at])) { return true } } } }; function BranchChunk(children) { - var this$1 = this; - this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; - ch.parent = this$1; + ch.parent = this; } this.size = size; this.height = height; @@ -5644,16 +5704,14 @@ chunkSize: function() { return this.size }, removeInner: function(at, n) { - var this$1 = this; - this.size -= n; for (var i = 0; i < this.children.length; ++i) { - var child = this$1.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); - this$1.height -= oldHeight - child.height; - if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) { break } at = 0; } else { at -= sz; } @@ -5670,18 +5728,14 @@ }, collapse: function(lines) { - var this$1 = this; - - for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } }, insertInner: function(at, lines, height) { - var this$1 = this; - this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { - var child = this$1.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { @@ -5691,11 +5745,11 @@ for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; - this$1.children.splice(++i, 0, leaf); - leaf.parent = this$1; + this.children.splice(++i, 0, leaf); + leaf.parent = this; } child.lines = child.lines.slice(0, remaining); - this$1.maybeSpill(); + this.maybeSpill(); } break } @@ -5727,10 +5781,8 @@ }, iterN: function(at, n, op) { - var this$1 = this; - for (var i = 0; i < this.children.length; ++i) { - var child = this$1.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) { return true } @@ -5744,20 +5796,16 @@ // Line widgets are block elements displayed above or below a line. var LineWidget = function(doc, node, options) { - var this$1 = this; - if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) - { this$1[opt] = options[opt]; } } } + { this[opt] = options[opt]; } } } this.doc = doc; this.node = node; }; LineWidget.prototype.clear = function () { - var this$1 = this; - var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) { return } - for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } if (!ws.length) { line.widgets = null; } var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); @@ -5800,7 +5848,7 @@ changeLine(doc, handle, "widget", function (line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) { widgets.push(widget); } - else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; @@ -5840,8 +5888,6 @@ // Clear the marker. TextMarker.prototype.clear = function () { - var this$1 = this; - if (this.explicitlyCleared) { return } var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) { startOperation(cm); } @@ -5851,19 +5897,19 @@ } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { - var line = this$1.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this$1); - if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } else if (cm) { if (span.to != null) { max = lineNo(line); } if (span.from != null) { min = lineNo(line); } } line.markedSpans = removeMarkedSpan(line.markedSpans, span); - if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) { updateLineHeight(line, textHeight(cm.display)); } } if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { - var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; @@ -5889,13 +5935,11 @@ // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function (side, lineObj) { - var this$1 = this; - if (side == null && this.type == "bookmark") { side = 1; } var from, to; for (var i = 0; i < this.lines.length; ++i) { - var line = this$1.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this$1); + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) { return from } @@ -5990,7 +6034,7 @@ if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, - curLine == to.line ? to.ch : null)); + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); ++curLine; }); // lineIsHidden depends on the presence of the spans, so needs a second pass @@ -6029,21 +6073,17 @@ // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = function(markers, primary) { - var this$1 = this; - this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) - { markers[i].parent = this$1; } + { markers[i].parent = this; } }; SharedTextMarker.prototype.clear = function () { - var this$1 = this; - if (this.explicitlyCleared) { return } this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) - { this$1.markers[i].clear(); } + { this.markers[i].clear(); } signalLater(this, "clear"); }; @@ -6166,6 +6206,7 @@ getRange: function(from, to, lineSep) { var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } return lines.join(lineSep || this.lineSeparator()) }, @@ -6186,11 +6227,11 @@ clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { - var range$$1 = this.sel.primary(), pos; - if (start == null || start == "head") { pos = range$$1.head; } - else if (start == "anchor") { pos = range$$1.anchor; } - else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } - else { pos = range$$1.from(); } + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } return pos }, listSelections: function() { return this.sel.ranges }, @@ -6213,13 +6254,11 @@ extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { - var this$1 = this; - if (!ranges.length) { return } var out = []; for (var i = 0; i < ranges.length; i++) - { out[i] = new Range(clipPos(this$1, ranges[i].anchor), - clipPos(this$1, ranges[i].head)); } + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } setSelection(this, normalizeSelection(this.cm, out, primary), options); }), @@ -6230,23 +6269,19 @@ }), getSelection: function(lineSep) { - var this$1 = this; - var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) { return lines } else { return lines.join(lineSep || this.lineSeparator()) } }, getSelections: function(lineSep) { - var this$1 = this; - var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); - if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } parts[i] = sel; } return parts @@ -6258,16 +6293,14 @@ this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { - var this$1 = this; - var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { - var range$$1 = sel.ranges[i]; - changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) - { makeChange(this$1, changes[i$1]); } + { makeChange(this, changes[i$1]); } if (newSel) { setSelectionReplaceHistory(this, newSel); } else if (this.cm) { ensureCursorVisible(this.cm); } }), @@ -6288,7 +6321,7 @@ clearHistory: function() { var this$1 = this; - this.history = new History(this.history.maxGeneration); + this.history = new History(this.history); linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); }, @@ -6309,7 +6342,7 @@ undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { - var hist = this.history = new History(this.history.maxGeneration); + var hist = this.history = new History(this.history); hist.done = copyHistoryArray(histData.done.slice(0), null, true); hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, @@ -6411,18 +6444,18 @@ }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); - var found = [], lineNo$$1 = from.line; + var found = [], lineNo = from.line; this.iter(from.line, to.line + 1, function (line) { var spans = line.markedSpans; if (spans) { for (var i = 0; i < spans.length; i++) { var span = spans[i]; - if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || - span.from == null && lineNo$$1 != from.line || - span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) { found.push(span.marker.parent || span.marker); } } } - ++lineNo$$1; + ++lineNo; }); return found }, @@ -6437,14 +6470,14 @@ }, posFromIndex: function(off) { - var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; this.iter(function (line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true } off -= sz; - ++lineNo$$1; + ++lineNo; }); - return clipPos(this, Pos(lineNo$$1, ch)) + return clipPos(this, Pos(lineNo, ch)) }, indexFromPos: function (coords) { coords = clipPos(this, coords); @@ -6483,15 +6516,13 @@ return copy }, unlinkDoc: function(other) { - var this$1 = this; - if (other instanceof CodeMirror) { other = other.doc; } if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { - var link = this$1.linked[i]; + var link = this.linked[i]; if (link.doc != other) { continue } - this$1.linked.splice(i, 1); - other.unlinkDoc(this$1); - detachSharedMarkers(findSharedMarkers(this$1)); + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); break } } // If the histories were shared, split them again @@ -6552,7 +6583,7 @@ text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); - setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); })(); } }; @@ -6597,7 +6628,7 @@ cm.display.input.focus(); } } - catch(e){} + catch(e$1){} } } @@ -6693,7 +6724,7 @@ 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", - 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; @@ -6730,10 +6761,9 @@ // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", - "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", - "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", - "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", - "Ctrl-O": "openLine" + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", @@ -6800,18 +6830,18 @@ return keymap } - function lookupKey(key, map$$1, handle, context) { - map$$1 = getKeyMap(map$$1); - var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; if (found === false) { return "nothing" } if (found === "...") { return "multi" } if (found != null && handle(found)) { return "handled" } - if (map$$1.fallthrough) { - if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") - { return lookupKey(key, map$$1.fallthrough, handle, context) } - for (var i = 0; i < map$$1.fallthrough.length; i++) { - var result = lookupKey(key, map$$1.fallthrough[i], handle, context); + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); if (result) { return result } } } @@ -6828,7 +6858,7 @@ var base = name; if (event.altKey && base != "Alt") { name = "Alt-" + name; } if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } - if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } return name } @@ -6885,7 +6915,7 @@ function endOfLine(visually, cm, lineObj, lineNo, dir) { if (visually) { - if (cm.getOption("direction") == "rtl") { dir = -dir; } + if (cm.doc.direction == "rtl") { dir = -dir; } var order = getOrder(lineObj, cm.doc.direction); if (order) { var part = dir < 0 ? lst(order) : order[0]; @@ -7054,7 +7084,7 @@ goGroupRight: function (cm) { return cm.moveH(1, "group"); }, goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, goWordRight: function (cm) { return cm.moveH(1, "word"); }, - delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, @@ -7140,7 +7170,7 @@ var line = getLine(cm.doc, start.line); var order = getOrder(line, cm.doc.direction); if (!order || order[0].level == 0) { - var firstNonWS = Math.max(0, line.text.search(/\S/)); + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } @@ -7243,7 +7273,8 @@ var lastStoppedKey = null; function onKeyDown(e) { var cm = this; - cm.curOp.focus = activeElt(); + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(root(cm)); if (signalDOMEvent(cm, e)) { return } // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } @@ -7286,6 +7317,7 @@ function onKeyPress(e) { var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} @@ -7349,7 +7381,7 @@ } if (clickInGutter(cm, e)) { return } var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; - window.focus(); + win(cm).focus(); // #3261: make sure, that we're not starting a second selection if (button == 1 && cm.state.selectingText) @@ -7404,7 +7436,7 @@ function leftButtonDown(cm, pos, repeat, event) { if (ie) { setTimeout(bind(ensureFocus, cm), 0); } - else { cm.curOp.focus = activeElt(); } + else { cm.curOp.focus = activeElt(root(cm)); } var behavior = configureMouse(cm, repeat, event); @@ -7425,6 +7457,10 @@ var dragEnd = operation(cm, function (e) { if (webkit) { display.scroller.draggable = false; } cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } off(display.wrapper.ownerDocument, "mouseup", dragEnd); off(display.wrapper.ownerDocument, "mousemove", mouseMove); off(display.scroller, "dragstart", dragStart); @@ -7434,8 +7470,8 @@ if (!behavior.addNew) { 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 () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); } + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } else { display.input.focus(); } } @@ -7448,15 +7484,15 @@ if (webkit) { display.scroller.draggable = true; } cm.state.draggingText = dragEnd; dragEnd.copy = !behavior.moveOnDrag; - // IE's approach to draggable - if (display.scroller.dragDrop) { display.scroller.dragDrop(); } on(display.wrapper.ownerDocument, "mouseup", dragEnd); on(display.wrapper.ownerDocument, "mousemove", mouseMove); on(display.scroller, "dragstart", dragStart); on(display.scroller, "drop", dragEnd); - delayBlurEvent(cm); + cm.state.delayingBlurEvent = true; setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } } function rangeForUnit(cm, pos, unit) { @@ -7469,6 +7505,7 @@ // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } var display = cm.display, doc = cm.doc; e_preventDefault(event); @@ -7489,11 +7526,11 @@ start = posFromMouse(cm, event, true, true); ourIndex = -1; } else { - var range$$1 = rangeForUnit(cm, start, behavior.unit); + var range = rangeForUnit(cm, start, behavior.unit); if (behavior.extend) - { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } else - { ourRange = range$$1; } + { ourRange = range; } } if (!behavior.addNew) { @@ -7536,14 +7573,14 @@ cm.scrollIntoView(pos); } else { var oldRange = ourRange; - var range$$1 = rangeForUnit(cm, pos, behavior.unit); + var range = rangeForUnit(cm, pos, behavior.unit); var anchor = oldRange.anchor, head; - if (cmp(range$$1.anchor, anchor) > 0) { - head = range$$1.head; - anchor = minPos(oldRange.from(), range$$1.anchor); + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); } else { - head = range$$1.anchor; - anchor = maxPos(oldRange.to(), range$$1.head); + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); } var ranges$1 = startSel.ranges.slice(0); ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); @@ -7563,7 +7600,7 @@ var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); if (!cur) { return } if (cmp(cur, lastPos) != 0) { - cm.curOp.focus = activeElt(); + cm.curOp.focus = activeElt(root(cm)); extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) @@ -7605,17 +7642,17 @@ // Used when mouse-selecting to adjust the anchor to the proper side // of a bidi jump depending on the visual position of the head. - function bidiSimplify(cm, range$$1) { - var anchor = range$$1.anchor; - var head = range$$1.head; + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; var anchorLine = getLine(cm.doc, anchor.line); - if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 } + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } var order = getOrder(anchorLine); - if (!order) { return range$$1 } + if (!order) { return range } var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; - if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 } + if (part.from != anchor.ch && part.to != anchor.ch) { return range } var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); - if (boundary == 0 || boundary == order.length) { return range$$1 } + if (boundary == 0 || boundary == order.length) { return range } // Compute the relative visual position of the head compared to the // anchor (<0 is to the left, >0 to the right) @@ -7634,7 +7671,7 @@ var usePart = order[boundary + (leftSide ? -1 : 0)]; var from = leftSide == (usePart.level == 1); var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; - return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head) + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) } @@ -7647,7 +7684,7 @@ mY = e.touches[0].clientY; } else { try { mX = e.clientX; mY = e.clientY; } - catch(e) { return false } + catch(e$1) { return false } } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (prevent) { e_preventDefault(e); } @@ -7747,7 +7784,7 @@ for (var i = newBreaks.length - 1; i >= 0; i--) { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } }); - option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != Init) { cm.refresh(); } }); @@ -7811,6 +7848,12 @@ } cm.display.input.readOnlyChanged(val); }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); @@ -7921,15 +7964,17 @@ attachDoc(this, doc); if ((options.autofocus && !mobile) || this.hasFocus()) - { setTimeout(bind(onFocus, this), 20); } + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } else { onBlur(this); } for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) - { optionHandlers[opt](this$1, options[opt], Init); } } + { optionHandlers[opt](this, options[opt], Init); } } maybeUpdateLineNumberWidth(this); if (options.finishInit) { options.finishInit(this); } - for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. @@ -8154,14 +8199,14 @@ var updateInput = cm.curOp.updateInput; // Normal behavior is to insert the new text into every selection for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { - var range$$1 = sel.ranges[i$1]; - var from = range$$1.from(), to = range$$1.to(); - if (range$$1.empty()) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { if (deleted && deleted > 0) // Handle deletion { from = Pos(from.line, from.ch - deleted); } else if (cm.state.overwrite && !paste) // Handle overwrite { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } - else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) { from = to = Pos(from.line, 0); } } var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, @@ -8182,7 +8227,7 @@ var pasted = e.clipboardData && e.clipboardData.getData("Text"); if (pasted) { e.preventDefault(); - if (!cm.isReadOnly() && !cm.options.disableInput) + if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus()) { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } return true } @@ -8194,21 +8239,21 @@ var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range$$1 = sel.ranges[i]; - if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } - var mode = cm.getModeAt(range$$1.head); + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { - indented = indentLine(cm, range$$1.head.line, "smart"); + indented = indentLine(cm, range.head.line, "smart"); break } } } else if (mode.electricInput) { - if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) - { indented = indentLine(cm, range$$1.head.line, "smart"); } + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } } - if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } } } @@ -8224,13 +8269,13 @@ } function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { - field.setAttribute("autocorrect", autocorrect ? "" : "off"); - field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("autocorrect", autocorrect ? "on" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "on" : "off"); field.setAttribute("spellcheck", !!spellcheck); } function hiddenTextarea() { - var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none"); var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling @@ -8240,7 +8285,6 @@ else { te.setAttribute("wrap", "off"); } // If border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) { te.style.border = "1px solid black"; } - disableBrowserMagic(te); return div } @@ -8259,7 +8303,7 @@ CodeMirror.prototype = { constructor: CodeMirror, - focus: function(){window.focus(); this.display.input.focus();}, + focus: function(){win(this).focus(); this.display.input.focus();}, setOption: function(option, value) { var options = this.options, old = options[option]; @@ -8273,13 +8317,13 @@ getOption: function(option) {return this.options[option]}, getDoc: function() {return this.doc}, - addKeyMap: function(map$$1, bottom) { - this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); }, - removeKeyMap: function(map$$1) { + removeKeyMap: function(map) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) - { if (maps[i] == map$$1 || maps[i].name == map$$1) { + { if (maps[i] == map || maps[i].name == map) { maps.splice(i, 1); return true } } @@ -8296,15 +8340,13 @@ regChange(this); }), removeOverlay: methodOp(function(spec) { - var this$1 = this; - var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); - this$1.state.modeGen++; - regChange(this$1); + this.state.modeGen++; + regChange(this); return } } @@ -8318,24 +8360,22 @@ if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } }), indentSelection: methodOp(function(how) { - var this$1 = this; - var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { - var range$$1 = ranges[i]; - if (!range$$1.empty()) { - var from = range$$1.from(), to = range$$1.to(); + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); var start = Math.max(end, from.line); - end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) - { indentLine(this$1, j, how); } - var newRanges = this$1.doc.sel.ranges; + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) - { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } - } else if (range$$1.head.line > end) { - indentLine(this$1, range$$1.head.line, how, true); - end = range$$1.head.line; - if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } } } }), @@ -8377,8 +8417,6 @@ }, getHelpers: function(pos, type) { - var this$1 = this; - var found = []; if (!helpers.hasOwnProperty(type)) { return found } var help = helpers[type], mode = this.getModeAt(pos); @@ -8396,7 +8434,7 @@ } for (var i$1 = 0; i$1 < help._global.length; i$1++) { var cur = help._global[i$1]; - if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) { found.push(cur.val); } } return found @@ -8409,10 +8447,10 @@ }, cursorCoords: function(start, mode) { - var pos, range$$1 = this.doc.sel.primary(); - if (start == null) { pos = range$$1.head; } + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } else if (typeof start == "object") { pos = clipPos(this.doc, start); } - else { pos = start ? range$$1.from() : range$$1.to(); } + else { pos = start ? range.from() : range.to(); } return cursorCoords(this, pos, mode || "page") }, @@ -8496,13 +8534,11 @@ triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { - var this$1 = this; - var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { - cur = findPosH(this$1.doc, cur, dir, unit, visually); + cur = findPosH(this.doc, cur, dir, unit, visually); if (cur.hitSide) { break } } return cur @@ -8511,11 +8547,11 @@ moveH: methodOp(function(dir, unit) { var this$1 = this; - this.extendSelectionsBy(function (range$$1) { - if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) - { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } else - { return dir < 0 ? range$$1.from() : range$$1.to() } + { return dir < 0 ? range.from() : range.to() } }, sel_move); }), @@ -8524,23 +8560,21 @@ if (sel.somethingSelected()) { doc.replaceSelection("", null, "+delete"); } else - { deleteNearSelection(this, function (range$$1) { - var other = findPosH(doc, range$$1.head, dir, unit, false); - return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} }); } }), findPosV: function(from, amount, unit, goalColumn) { - var this$1 = this; - var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { - var coords = cursorCoords(this$1, cur, "div"); + var coords = cursorCoords(this, cur, "div"); if (x == null) { x = coords.left; } else { coords.left = x; } - cur = findPosV(this$1, coords, dir, unit); + cur = findPosV(this, coords, dir, unit); if (cur.hitSide) { break } } return cur @@ -8551,14 +8585,14 @@ var doc = this.doc, goals = []; var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); - doc.extendSelectionsBy(function (range$$1) { + doc.extendSelectionsBy(function (range) { if (collapse) - { return dir < 0 ? range$$1.from() : range$$1.to() } - var headPos = cursorCoords(this$1, range$$1.head, "div"); - if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } goals.push(headPos.left); var pos = findPosV(this$1, headPos, dir, unit); - if (unit == "page" && range$$1 == doc.sel.primary()) + if (unit == "page" && range == doc.sel.primary()) { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } return pos }, sel_move); @@ -8593,7 +8627,7 @@ signal(this, "overwriteToggle", this, this.state.overwrite); }, - hasFocus: function() { return this.display.input.getField() == activeElt() }, + hasFocus: function() { return this.display.input.getField() == activeElt(root(this)) }, isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), @@ -8605,22 +8639,22 @@ clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, - scrollIntoView: methodOp(function(range$$1, margin) { - if (range$$1 == null) { - range$$1 = {from: this.doc.sel.primary().head, to: null}; + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; if (margin == null) { margin = this.options.cursorScrollMargin; } - } else if (typeof range$$1 == "number") { - range$$1 = {from: Pos(range$$1, 0), to: null}; - } else if (range$$1.from == null) { - range$$1 = {from: range$$1, to: null}; + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; } - if (!range$$1.to) { range$$1.to = range$$1.from; } - range$$1.margin = margin || 0; + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; - if (range$$1.from.line != null) { - scrollToRange(this, range$$1); + if (range.from.line != null) { + scrollToRange(this, range); } else { - scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); + scrollToCoordsRange(this, range.from, range.to, range.margin); } }), @@ -8631,11 +8665,11 @@ if (width != null) { this.display.wrapper.style.width = interpret(width); } if (height != null) { this.display.wrapper.style.height = interpret(height); } if (this.options.lineWrapping) { clearLineMeasurementCache(this); } - var lineNo$$1 = this.display.viewFrom; - this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) - { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } - ++lineNo$$1; + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; }); this.curOp.forceUpdate = true; signal(this, "refresh", this); @@ -8652,7 +8686,7 @@ clearCaches(this); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this.display); - if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) { estimateLineHeights(this); } signal(this, "refresh", this); }), @@ -8694,19 +8728,19 @@ } // Used for horizontal relative motion. Dir is -1 or 1 (left or - // right), unit can be "char", "column" (like char, but doesn't - // cross line boundaries), "word" (across next word), or "group" (to - // the start of next group of word or non-word-non-whitespace - // chars). The visually param controls whether, in right-to-left - // text, direction 1 means to move towards the next index in the - // string, or towards the character to the right of the current - // position. The resulting position will have a hitSide=true - // property if it reached the end of the document. + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var oldPos = pos; var origDir = dir; var lineObj = getLine(doc, pos.line); - var lineDir = visually && doc.cm && doc.cm.getOption("direction") == "rtl" ? -dir : dir; + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; function findNextLine() { var l = pos.line + lineDir; if (l < doc.first || l >= doc.first + doc.size) { return false } @@ -8715,7 +8749,15 @@ } function moveOnce(boundToLine) { var next; - if (visually) { + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir); } else { next = moveLogically(lineObj, pos, dir); @@ -8731,7 +8773,7 @@ return true } - if (unit == "char") { + if (unit == "char" || unit == "codepoint") { moveOnce(); } else if (unit == "column") { moveOnce(true); @@ -8766,7 +8808,7 @@ function findPosV(cm, pos, dir, unit) { var doc = cm.doc, x = pos.left, y; if (unit == "page") { - var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight); var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; @@ -8799,10 +8841,19 @@ var input = this, cm = input.cm; var div = input.div = display.lineDiv; + div.contentEditable = true; disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + on(div, "paste", function (e) { - if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } }); @@ -8827,7 +8878,7 @@ }); function onCopyCut(e) { - if (signalDOMEvent(cm, e)) { return } + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } @@ -8855,9 +8906,10 @@ } // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; + disableBrowserMagic(te); cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); te.value = lastCopied.text.join("\n"); - var hadFocus = document.activeElement; + var hadFocus = activeElt(rootNode(div)); selectInput(te); setTimeout(function () { cm.display.lineSpace.removeChild(kludge); @@ -8869,9 +8921,18 @@ on(div, "cut", onCopyCut); }; + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + ContentEditableInput.prototype.prepareSelection = function () { var result = prepareSelection(this.cm, false); - result.focus = this.cm.state.focused; + result.focus = activeElt(rootNode(this.div)) == this.div; return result }; @@ -8907,8 +8968,8 @@ var end = to.line < cm.display.viewTo && posToDOM(cm, to); if (!end) { var measure = view[view.length - 1].measure; - var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; - end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; } if (!start || !end) { @@ -8967,7 +9028,7 @@ ContentEditableInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor") { - if (!this.selectionInEditor()) + if (!this.selectionInEditor() || activeElt(rootNode(this.div)) != this.div) { this.showSelection(this.prepareSelection(), true); } this.div.focus(); } @@ -8978,9 +9039,11 @@ ContentEditableInput.prototype.supportsTouch = function () { return true }; ContentEditableInput.prototype.receivedFocus = function () { + var this$1 = this; + var input = this; if (this.selectionInEditor()) - { this.pollSelection(); } + { setTimeout(function () { return this$1.pollSelection(); }, 20); } else { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } @@ -9197,11 +9260,11 @@ addText(cmText); return } - var markerID = node.getAttribute("cm-marker"), range$$1; + var markerID = node.getAttribute("cm-marker"), range; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); - if (found.length && (range$$1 = found[0].find(0))) - { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } return } if (node.getAttribute("contenteditable") == "false") { return } @@ -9269,13 +9332,13 @@ function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { - var map$$1 = i < 0 ? measure.map : maps[i]; - for (var j = 0; j < map$$1.length; j += 3) { - var curNode = map$$1[j + 2]; + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); - var ch = map$$1[j] + offset; - if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } return Pos(line, ch) } } @@ -9317,6 +9380,7 @@ // Used to work around IE issue with selection being forgotten when focus moves away from textarea this.hasSelection = false; this.composing = null; + this.resetting = false; }; TextareaInput.prototype.init = function (display) { @@ -9407,6 +9471,17 @@ // The semihidden textarea that is focused when the editor is // focused, and receives input. this.textarea = this.wrapper.firstChild; + var opts = this.cm.options; + disableBrowserMagic(this.textarea, opts.spellcheck, opts.autocorrect, opts.autocapitalize); + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } }; TextareaInput.prototype.prepareSelection = function () { @@ -9440,8 +9515,9 @@ // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) TextareaInput.prototype.reset = function (typing) { - if (this.contextMenuPending || this.composing) { return } + if (this.contextMenuPending || this.composing && typing) { return } var cm = this.cm; + this.resetting = true; if (cm.somethingSelected()) { this.prevInput = ""; var content = cm.getSelection(); @@ -9452,6 +9528,7 @@ this.prevInput = this.textarea.value = ""; if (ie && ie_version >= 9) { this.hasSelection = null; } } + this.resetting = false; }; TextareaInput.prototype.getField = function () { return this.textarea }; @@ -9459,7 +9536,7 @@ TextareaInput.prototype.supportsTouch = function () { return false }; TextareaInput.prototype.focus = function () { - if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt(rootNode(this.textarea)) != this.textarea)) { try { this.textarea.focus(); } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } @@ -9513,7 +9590,7 @@ // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, // in which case reading its value would be expensive. - if (this.contextMenuPending || !cm.state.focused || + if (this.contextMenuPending || this.resetting || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) { return false } @@ -9582,9 +9659,9 @@ input.wrapper.style.cssText = "position: static"; te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; var oldScrollY; - if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712) display.input.focus(); - if (webkit) { window.scrollTo(null, oldScrollY); } + if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); } display.input.reset(); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } @@ -9649,6 +9726,7 @@ TextareaInput.prototype.readOnlyChanged = function (val) { if (!val) { this.reset(); } this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; }; TextareaInput.prototype.setUneditable = function () {}; @@ -9665,7 +9743,7 @@ // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { - var hasFocus = activeElt(); + var hasFocus = activeElt(rootNode(textarea)); options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body; } @@ -9799,7 +9877,7 @@ addLegacyProps(CodeMirror); - CodeMirror.version = "5.51.0"; + CodeMirror.version = "5.65.21"; return CodeMirror; diff --git a/lib/redactor/codemirror/mode/clike/clike.js b/lib/redactor/codemirror/mode/clike/clike.js index 37da2ec..30c8f6e 100644 --- a/lib/redactor/codemirror/mode/clike/clike.js +++ b/lib/redactor/codemirror/mode/clike/clike.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -82,15 +82,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } - if (isPunctuationChar.test(ch)) { - curPunc = ch; - return null; - } if (numberStart.test(ch)) { stream.backUp(1) if (stream.match(number)) return "number" stream.next() } + if (isPunctuationChar.test(ch)) { + curPunc = ch; + return null; + } if (ch == "/") { if (stream.eat("*")) { state.tokenize = tokenComment; @@ -218,7 +218,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { }, indent: function(state, textAfter) { - if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass; + if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine && isTopScope(state.context)) + return CodeMirror.Pass; var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); var closing = firstChar == ctx.type; if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; @@ -350,8 +351,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { function cpp11StringHook(stream, state) { stream.backUp(1); // Raw strings. - if (stream.match(/(R|u8R|uR|UR|LR)/)) { - var match = stream.match(/"([^\s\\()]{0,16})\(/); + if (stream.match(/^(?:R|u8R|uR|UR|LR)/)) { + var match = stream.match(/^"([^\s\\()]{0,16})\(/); if (!match) { return false; } @@ -360,8 +361,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { return tokenRawString(stream, state); } // Unicode strings/chars. - if (stream.match(/(u8|u|U|L)/)) { - if (stream.match(/["']/, /* eat */ false)) { + if (stream.match(/^(?:u8|u|U|L)/)) { + if (stream.match(/^["']/, /* eat */ false)) { return "string"; } return false; @@ -484,7 +485,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "instanceof interface native new package private protected public " + "return static strictfp super switch synchronized this throw throws transient " + "try volatile while @interface"), - types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " + + types: words("var byte short int long float double boolean char void Boolean Byte Character Double Float " + "Integer Long Number Object Short String StringBuffer StringBuilder Void"), blockKeywords: words("catch class do else finally for if switch try while"), defKeywords: words("class interface enum @interface"), @@ -498,6 +499,11 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { stream.eatWhile(/[\w\$_]/); return "meta"; + }, + '"': function(stream, state) { + if (!stream.match(/""$/)) return false; + state.tokenize = tokenTripleString; + return state.tokenize(stream, state); } }, modeProps: {fold: ["brace", "import"]} @@ -507,8 +513,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { name: "clike", keywords: words("abstract as async await base break case catch checked class const continue" + " default delegate do else enum event explicit extern finally fixed for" + - " foreach goto if implicit in interface internal is lock namespace new" + - " operator out override params private protected public readonly ref return sealed" + + " foreach goto if implicit in init interface internal is lock namespace new" + + " operator out override params private protected public readonly record ref required return sealed" + " sizeof stackalloc static struct switch this throw try typeof unchecked" + " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + " global group into join let orderby partial remove select set value var yield"), @@ -517,7 +523,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { " UInt64 bool byte char decimal double short int long object" + " sbyte float string ushort uint ulong"), blockKeywords: words("catch class do else finally for foreach if struct switch try while"), - defKeywords: words("class interface namespace struct var"), + defKeywords: words("class interface namespace record struct var"), typeFirstDefinitions: true, atoms: words("true false null"), hooks: { @@ -608,6 +614,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { return state.tokenize(stream, state); }, "'": function(stream) { + if (stream.match(/^(\\[^'\s]+|[^\\'])'/)) return "string-2" stream.eatWhile(/[\w\$_\xa1-\uffff]/); return "atom"; }, @@ -659,7 +666,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + - "external annotation crossinline const operator infix suspend actual expect setparam" + "external annotation crossinline const operator infix suspend actual expect setparam value" ), types: words( /* package java.lang */ @@ -670,10 +677,10 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " + "LazyThreadSafetyMode LongArray Nothing ShortArray Unit" ), - intendSwitch: false, + indentSwitch: false, indentStatements: false, multiLineStrings: true, - number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, + number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(ul?|l|f)?/i, blockKeywords: words("catch class do else finally for if where try while enum"), defKeywords: words("class val var object interface fun"), atoms: words("true false null this"), @@ -749,7 +756,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " + "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " + "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " + - "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " + + "gl_TextureMatrixTranspose gl_ModelViewMatrixInverseTranspose " + "gl_ProjectionMatrixInverseTranspose " + "gl_ModelViewProjectionMatrixInverseTranspose " + "gl_TextureMatrixInverseTranspose " + diff --git a/lib/redactor/codemirror/mode/clike/index.html b/lib/redactor/codemirror/mode/clike/index.html new file mode 100644 index 0000000..5b82df3 --- /dev/null +++ b/lib/redactor/codemirror/mode/clike/index.html @@ -0,0 +1,380 @@ + + +CodeMirror: C-like mode + + + + + + + + + + + + +
+

C-like mode

+ +
+ +

C++ example

+ +
+ +

Objective-C example

+ +
+ +

Java example

+ +
+ +

Scala example

+ +
+ +

Kotlin mode

+ +
+ +

Ceylon mode

+ +
+ + + +

Simple mode that tries to handle C-like languages as well as it + can. Takes two configuration parameters: keywords, an + object whose property names are the keywords in the language, + and useCPP, which determines whether C preprocessor + directives are recognized.

+ +

MIME types defined: text/x-csrc + (C), text/x-c++src (C++), text/x-java + (Java), text/x-csharp (C#), + text/x-objectivec (Objective-C), + text/x-scala (Scala), text/x-vertex + x-shader/x-fragment (shader programs), + text/x-squirrel (Squirrel) and + text/x-ceylon (Ceylon)

+
diff --git a/lib/redactor/codemirror/mode/clike/scala.html b/lib/redactor/codemirror/mode/clike/scala.html new file mode 100644 index 0000000..49eab45 --- /dev/null +++ b/lib/redactor/codemirror/mode/clike/scala.html @@ -0,0 +1,767 @@ + + +CodeMirror: Scala mode + + + + + + + + + + +
+

Scala mode

+
+ +
+ + +
diff --git a/lib/redactor/codemirror/mode/clike/test.js b/lib/redactor/codemirror/mode/clike/test.js new file mode 100644 index 0000000..2933a00 --- /dev/null +++ b/lib/redactor/codemirror/mode/clike/test.js @@ -0,0 +1,170 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-c"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT("indent", + "[type void] [def foo]([type void*] [variable a], [type int] [variable b]) {", + " [type int] [variable c] [operator =] [variable b] [operator +]", + " [number 1];", + " [keyword return] [operator *][variable a];", + "}"); + + MT("indent_switch", + "[keyword switch] ([variable x]) {", + " [keyword case] [number 10]:", + " [keyword return] [number 20];", + " [keyword default]:", + " [variable printf]([string \"foo %c\"], [variable x]);", + "}"); + + MT("def", + "[type void] [def foo]() {}", + "[keyword struct] [def bar]{}", + "[keyword enum] [def zot]{}", + "[keyword union] [def ugh]{}", + "[type int] [type *][def baz]() {}"); + + MT("def_new_line", + "::[variable std]::[variable SomeTerribleType][operator <][variable T][operator >]", + "[def SomeLongMethodNameThatDoesntFitIntoOneLine]([keyword const] [variable MyType][operator &] [variable param]) {}") + + MT("double_block", + "[keyword for] (;;)", + " [keyword for] (;;)", + " [variable x][operator ++];", + "[keyword return];"); + + MT("preprocessor", + "[meta #define FOO 3]", + "[type int] [variable foo];", + "[meta #define BAR\\]", + "[meta 4]", + "[type unsigned] [type int] [variable bar] [operator =] [number 8];", + "[meta #include ][comment // comment]") + + MT("c_underscores", + "[builtin __FOO];", + "[builtin _Complex];", + "[builtin __aName];", + "[variable _aName];"); + + MT("c_types", + "[type int];", + "[type long];", + "[type char];", + "[type short];", + "[type double];", + "[type float];", + "[type unsigned];", + "[type signed];", + "[type void];", + "[type bool];", + "[type foo_t];", + "[variable foo_T];", + "[variable _t];"); + + var mode_cpp = CodeMirror.getMode({indentUnit: 2}, "text/x-c++src"); + function MTCPP(name) { test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1)); } + + MTCPP("cpp14_literal", + "[number 10'000];", + "[number 0b10'000];", + "[number 0x10'000];", + "[string '100000'];"); + + MTCPP("ctor_dtor", + "[def Foo::Foo]() {}", + "[def Foo::~Foo]() {}"); + + MTCPP("cpp_underscores", + "[builtin __FOO];", + "[builtin _Complex];", + "[builtin __aName];", + "[variable _aName];"); + + var mode_objc = CodeMirror.getMode({indentUnit: 2}, "text/x-objectivec"); + function MTOBJC(name) { test.mode(name, mode_objc, Array.prototype.slice.call(arguments, 1)); } + + MTOBJC("objc_underscores", + "[builtin __FOO];", + "[builtin _Complex];", + "[builtin __aName];", + "[variable _aName];"); + + MTOBJC("objc_interface", + "[keyword @interface] [def foo] {", + " [type int] [variable bar];", + "}", + "[keyword @property] ([keyword atomic], [keyword nullable]) [variable NSString][operator *] [variable a];", + "[keyword @property] ([keyword nonatomic], [keyword assign]) [type int] [variable b];", + "[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] " + + "[builtin NS_DESIGNATED_INITIALIZER];", + "[keyword @end]"); + + MTOBJC("objc_implementation", + "[keyword @implementation] [def foo] {", + " [type int] [variable bar];", + "}", + "[keyword @property] ([keyword readwrite]) [type SEL] [variable a];", + "[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] {", + " [keyword if](([keyword self] [operator =] [[[keyword super] [variable init] ]])) {}", + " [keyword return] [keyword self];", + "}", + "[keyword @end]"); + + MTOBJC("objc_types", + "[type int];", + "[type foo_t];", + "[variable foo_T];", + "[type id];", + "[type SEL];", + "[type instancetype];", + "[type Class];", + "[type Protocol];", + "[type BOOL];" + ); + + var mode_scala = CodeMirror.getMode({indentUnit: 2}, "text/x-scala"); + function MTSCALA(name) { test.mode("scala_" + name, mode_scala, Array.prototype.slice.call(arguments, 1)); } + MTSCALA("nested_comments", + "[comment /*]", + "[comment But wait /* this is a nested comment */ for real]", + "[comment /**** let * me * show * you ****/]", + "[comment ///// let / me / show / you /////]", + "[comment */]"); + + var mode_java = CodeMirror.getMode({indentUnit: 2}, "text/x-java"); + function MTJAVA(name) { test.mode("java_" + name, mode_java, Array.prototype.slice.call(arguments, 1)); } + MTJAVA("types", + "[type byte];", + "[type short];", + "[type int];", + "[type long];", + "[type float];", + "[type double];", + "[type boolean];", + "[type char];", + "[type void];", + "[type Boolean];", + "[type Byte];", + "[type Character];", + "[type Double];", + "[type Float];", + "[type Integer];", + "[type Long];", + "[type Number];", + "[type Object];", + "[type Short];", + "[type String];", + "[type StringBuffer];", + "[type StringBuilder];", + "[type Void];"); + + MTJAVA("indent", + "[keyword public] [keyword class] [def A] [keyword extends] [variable B]", + "{", + " [variable c]()") +})(); diff --git a/lib/redactor/codemirror/mode/css/css.js b/lib/redactor/codemirror/mode/css/css.js index 05742c5..b0721b4 100644 --- a/lib/redactor/codemirror/mode/css/css.js +++ b/lib/redactor/codemirror/mode/css/css.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -29,7 +29,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { valueKeywords = parserConfig.valueKeywords || {}, allowNested = parserConfig.allowNested, lineComment = parserConfig.lineComment, - supportsAtComponent = parserConfig.supportsAtComponent === true; + supportsAtComponent = parserConfig.supportsAtComponent === true, + highlightNonStandardPropertyKeywords = config.highlightNonStandardPropertyKeywords !== false; var type, override; function ret(style, tp) { type = tp; return style; } @@ -77,8 +78,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return ret("qualifier", "qualifier"); } else if (/[:;{}\[\]\(\)]/.test(ch)) { return ret(null, ch); - } else if (stream.match(/[\w-.]+(?=\()/)) { - if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) { + } else if (stream.match(/^[\w-.]+(?=\()/)) { + if (/^(url(-prefix)?|domain|regexp)$/i.test(stream.current())) { state.tokenize = tokenParenthesized; } return ret("variable callee", "variable"); @@ -107,7 +108,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { function tokenParenthesized(stream, state) { stream.next(); // Must be '(' - if (!stream.match(/\s*[\"\')]/, false)) + if (!stream.match(/^\s*[\"\')]/, false)) state.tokenize = tokenString(")"); else state.tokenize = null; @@ -197,7 +198,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { override = "property"; return "maybeprop"; } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) { - override = "string-2"; + override = highlightNonStandardPropertyKeywords ? "string-2" : "property"; return "maybeprop"; } else if (allowNested) { override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag"; @@ -227,7 +228,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { if (type == "}" || type == "{") return popAndPass(type, stream, state); if (type == "(") return pushContext(state, stream, "parens"); - if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) { + if (type == "hash" && !/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(stream.current())) { override += " error"; } else if (type == "word") { wordAsValue(stream); @@ -291,7 +292,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { else if (propertyKeywords.hasOwnProperty(word)) override = "property"; else if (nonStandardPropertyKeywords.hasOwnProperty(word)) - override = "string-2"; + override = highlightNonStandardPropertyKeywords ? "string-2" : "property"; else if (valueKeywords.hasOwnProperty(word)) override = "atom"; else if (colorKeywords.hasOwnProperty(word)) @@ -442,117 +443,151 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "monochrome", "min-monochrome", "max-monochrome", "resolution", "min-resolution", "max-resolution", "scan", "grid", "orientation", "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio", - "pointer", "any-pointer", "hover", "any-hover" + "pointer", "any-pointer", "hover", "any-hover", "prefers-color-scheme", + "dynamic-range", "video-dynamic-range" ], mediaFeatures = keySet(mediaFeatures_); var mediaValueKeywords_ = [ "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover", - "interlace", "progressive" + "interlace", "progressive", + "dark", "light", + "standard", "high" ], mediaValueKeywords = keySet(mediaValueKeywords_); var propertyKeywords_ = [ "align-content", "align-items", "align-self", "alignment-adjust", - "alignment-baseline", "anchor-point", "animation", "animation-delay", + "alignment-baseline", "all", "anchor-point", "animation", "animation-delay", "animation-direction", "animation-duration", "animation-fill-mode", "animation-iteration-count", "animation-name", "animation-play-state", - "animation-timing-function", "appearance", "azimuth", "backface-visibility", - "background", "background-attachment", "background-blend-mode", "background-clip", - "background-color", "background-image", "background-origin", "background-position", - "background-repeat", "background-size", "baseline-shift", "binding", - "bleed", "bookmark-label", "bookmark-level", "bookmark-state", - "bookmark-target", "border", "border-bottom", "border-bottom-color", - "border-bottom-left-radius", "border-bottom-right-radius", - "border-bottom-style", "border-bottom-width", "border-collapse", - "border-color", "border-image", "border-image-outset", + "animation-timing-function", "appearance", "azimuth", "backdrop-filter", + "backface-visibility", "background", "background-attachment", + "background-blend-mode", "background-clip", "background-color", + "background-image", "background-origin", "background-position", + "background-position-x", "background-position-y", "background-repeat", + "background-size", "baseline-shift", "binding", "bleed", "block-size", + "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target", + "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius", + "border-bottom-right-radius", "border-bottom-style", "border-bottom-width", + "border-collapse", "border-color", "border-image", "border-image-outset", "border-image-repeat", "border-image-slice", "border-image-source", - "border-image-width", "border-left", "border-left-color", - "border-left-style", "border-left-width", "border-radius", "border-right", - "border-right-color", "border-right-style", "border-right-width", - "border-spacing", "border-style", "border-top", "border-top-color", - "border-top-left-radius", "border-top-right-radius", "border-top-style", - "border-top-width", "border-width", "bottom", "box-decoration-break", - "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", - "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count", - "column-fill", "column-gap", "column-rule", "column-rule-color", - "column-rule-style", "column-rule-width", "column-span", "column-width", - "columns", "content", "counter-increment", "counter-reset", "crop", "cue", - "cue-after", "cue-before", "cursor", "direction", "display", - "dominant-baseline", "drop-initial-after-adjust", - "drop-initial-after-align", "drop-initial-before-adjust", - "drop-initial-before-align", "drop-initial-size", "drop-initial-value", - "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", - "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", - "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", - "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", - "font-stretch", "font-style", "font-synthesis", "font-variant", - "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", - "font-variant-ligatures", "font-variant-numeric", "font-variant-position", - "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", - "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap", - "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", - "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns", - "grid-template-rows", "hanging-punctuation", "height", "hyphens", - "icon", "image-orientation", "image-rendering", "image-resolution", - "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing", - "line-break", "line-height", "line-stacking", "line-stacking-ruby", + "border-image-width", "border-left", "border-left-color", "border-left-style", + "border-left-width", "border-radius", "border-right", "border-right-color", + "border-right-style", "border-right-width", "border-spacing", "border-style", + "border-top", "border-top-color", "border-top-left-radius", + "border-top-right-radius", "border-top-style", "border-top-width", + "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing", + "break-after", "break-before", "break-inside", "caption-side", "caret-color", + "clear", "clip", "color", "color-profile", "column-count", "column-fill", + "column-gap", "column-rule", "column-rule-color", "column-rule-style", + "column-rule-width", "column-span", "column-width", "columns", "contain", + "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after", + "cue-before", "cursor", "direction", "display", "dominant-baseline", + "drop-initial-after-adjust", "drop-initial-after-align", + "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size", + "drop-initial-value", "elevation", "empty-cells", "fit", "fit-content", "fit-position", + "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow", + "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into", + "font", "font-family", "font-feature-settings", "font-kerning", + "font-language-override", "font-optical-sizing", "font-size", + "font-size-adjust", "font-stretch", "font-style", "font-synthesis", + "font-variant", "font-variant-alternates", "font-variant-caps", + "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", + "font-variant-position", "font-variation-settings", "font-weight", "gap", + "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows", + "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start", + "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start", + "grid-template", "grid-template-areas", "grid-template-columns", + "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon", + "image-orientation", "image-rendering", "image-resolution", "inline-box-align", + "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline", + "inset-inline-end", "inset-inline-start", "isolation", "justify-content", + "justify-items", "justify-self", "left", "letter-spacing", "line-break", + "line-height", "line-height-step", "line-stacking", "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", "list-style-position", "list-style-type", "margin", - "margin-bottom", "margin-left", "margin-right", "margin-top", - "marks", "marquee-direction", "marquee-loop", - "marquee-play-count", "marquee-speed", "marquee-style", "max-height", - "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index", - "nav-left", "nav-right", "nav-up", "object-fit", "object-position", - "opacity", "order", "orphans", "outline", - "outline-color", "outline-offset", "outline-style", "outline-width", - "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", - "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", - "page", "page-break-after", "page-break-before", "page-break-inside", - "page-policy", "pause", "pause-after", "pause-before", "perspective", - "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position", - "presentation-level", "punctuation-trim", "quotes", "region-break-after", - "region-break-before", "region-break-inside", "region-fragment", - "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", - "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", - "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin", - "shape-outside", "size", "speak", "speak-as", "speak-header", - "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", - "tab-size", "table-layout", "target", "target-name", "target-new", - "target-position", "text-align", "text-align-last", "text-decoration", + "margin-bottom", "margin-left", "margin-right", "margin-top", "marks", + "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", + "marquee-style", "mask-clip", "mask-composite", "mask-image", "mask-mode", + "mask-origin", "mask-position", "mask-repeat", "mask-size","mask-type", + "max-block-size", "max-height", "max-inline-size", + "max-width", "min-block-size", "min-height", "min-inline-size", "min-width", + "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", + "nav-up", "object-fit", "object-position", "offset", "offset-anchor", + "offset-distance", "offset-path", "offset-position", "offset-rotate", + "opacity", "order", "orphans", "outline", "outline-color", "outline-offset", + "outline-style", "outline-width", "overflow", "overflow-style", + "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom", + "padding-left", "padding-right", "padding-top", "page", "page-break-after", + "page-break-before", "page-break-inside", "page-policy", "pause", + "pause-after", "pause-before", "perspective", "perspective-origin", "pitch", + "pitch-range", "place-content", "place-items", "place-self", "play-during", + "position", "presentation-level", "punctuation-trim", "quotes", + "region-break-after", "region-break-before", "region-break-inside", + "region-fragment", "rendering-intent", "resize", "rest", "rest-after", + "rest-before", "richness", "right", "rotate", "rotation", "rotation-point", + "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span", + "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block", + "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom", + "scroll-margin-inline", "scroll-margin-inline-end", + "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right", + "scroll-margin-top", "scroll-padding", "scroll-padding-block", + "scroll-padding-block-end", "scroll-padding-block-start", + "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end", + "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right", + "scroll-padding-top", "scroll-snap-align", "scroll-snap-type", + "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside", + "size", "speak", "speak-as", "speak-header", "speak-numeral", + "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size", + "table-layout", "target", "target-name", "target-new", "target-position", + "text-align", "text-align-last", "text-combine-upright", "text-decoration", "text-decoration-color", "text-decoration-line", "text-decoration-skip", - "text-decoration-style", "text-emphasis", "text-emphasis-color", - "text-emphasis-position", "text-emphasis-style", "text-height", - "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", - "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", - "text-wrap", "top", "transform", "transform-origin", "transform-style", - "transition", "transition-delay", "transition-duration", - "transition-property", "transition-timing-function", "unicode-bidi", - "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration", - "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", - "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break", - "word-spacing", "word-wrap", "z-index", + "text-decoration-skip-ink", "text-decoration-style", "text-emphasis", + "text-emphasis-color", "text-emphasis-position", "text-emphasis-style", + "text-height", "text-indent", "text-justify", "text-orientation", + "text-outline", "text-overflow", "text-rendering", "text-shadow", + "text-size-adjust", "text-space-collapse", "text-transform", + "text-underline-position", "text-wrap", "top", "touch-action", "transform", "transform-origin", + "transform-style", "transition", "transition-delay", "transition-duration", + "transition-property", "transition-timing-function", "translate", + "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance", + "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate", + "voice-stress", "voice-volume", "volume", "white-space", "widows", "width", + "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index", // SVG-specific "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", "color-interpolation", "color-interpolation-filters", "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", - "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", + "marker", "marker-end", "marker-mid", "marker-start", "paint-order", "shape-rendering", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", - "glyph-orientation-vertical", "text-anchor", "writing-mode" + "glyph-orientation-vertical", "text-anchor", "writing-mode", ], propertyKeywords = keySet(propertyKeywords_); var nonStandardPropertyKeywords_ = [ + "accent-color", "aspect-ratio", "border-block", "border-block-color", "border-block-end", + "border-block-end-color", "border-block-end-style", "border-block-end-width", + "border-block-start", "border-block-start-color", "border-block-start-style", + "border-block-start-width", "border-block-style", "border-block-width", + "border-inline", "border-inline-color", "border-inline-end", + "border-inline-end-color", "border-inline-end-style", + "border-inline-end-width", "border-inline-start", "border-inline-start-color", + "border-inline-start-style", "border-inline-start-width", + "border-inline-style", "border-inline-width", "content-visibility", "margin-block", + "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end", + "margin-inline-start", "overflow-anchor", "overscroll-behavior", "padding-block", "padding-block-end", + "padding-block-start", "padding-inline", "padding-inline-end", + "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color", "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color", "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color", - "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside", - "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", - "searchfield-results-decoration", "zoom" + "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration", + "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom" ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_); var fontProperties_ = [ - "font-family", "src", "unicode-range", "font-variant", "font-feature-settings", - "font-stretch", "font-weight", "font-style" + "font-display", "font-family", "src", "unicode-range", "font-variant", + "font-feature-settings", "font-stretch", "font-weight", "font-style" ], fontProperties = keySet(fontProperties_); var counterDescriptors_ = [ @@ -565,16 +600,16 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", - "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", + "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", - "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", - "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", + "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", + "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", - "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", - "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", + "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink", + "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", @@ -584,7 +619,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", - "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", + "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen" ], colorKeywords = keySet(colorKeywords_); @@ -594,22 +629,22 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page", - "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary", - "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", - "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel", + "avoid-region", "axis-pan", "background", "backwards", "baseline", "below", "bidi-override", "binary", + "bengali", "blink", "block", "block-axis", "blur", "bold", "bolder", "border", "border-box", + "both", "bottom", "break", "break-all", "break-word", "brightness", "bullets", "button", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian", "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse", - "compact", "condensed", "contain", "content", "contents", - "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop", - "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal", + "compact", "condensed", "conic-gradient", "contain", "content", "contents", + "content-box", "context-menu", "continuous", "contrast", "copy", "counter", "counters", "cover", "crop", + "cross", "crosshair", "cubic-bezier", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal", "decimal-leading-zero", "default", "default-button", "dense", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "difference", "disc", "discard", "disclosure-closed", "disclosure-open", "document", "dot-dash", "dot-dot-dash", - "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", + "dotted", "double", "down", "drop-shadow", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", @@ -618,11 +653,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed", - "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes", - "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove", + "extra-expanded", "fantasy", "fast", "fill", "fill-box", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes", + "forwards", "from", "geometricPrecision", "georgian", "grayscale", "graytext", "grid", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew", "help", "hidden", "hide", "higher", "highlight", "highlighttext", - "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore", + "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "hue-rotate", "icon", "ignore", "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert", @@ -633,41 +668,37 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", - "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d", - "media-controls-background", "media-current-time-display", - "media-fullscreen-button", "media-mute-button", "media-play-button", - "media-return-to-realtime-button", "media-rewind-button", - "media-seek-back-button", "media-seek-forward-button", "media-slider", - "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", - "media-volume-slider-container", "media-volume-sliderthumb", "medium", - "menu", "menulist", "menulist-button", "menulist-text", - "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", - "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize", + "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "manipulation", "match", "matrix", "matrix3d", + "media-play-button", "media-slider", "media-sliderthumb", + "media-volume-slider", "media-volume-sliderthumb", "medium", + "menu", "menulist", "menulist-button", + "menutext", "message-box", "middle", "min-intrinsic", + "mix", "mongolian", "monospace", "move", "multiple", "multiple_mask_images", "multiply", "myanmar", "n-resize", "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote", "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", - "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter", + "painted", "page", "paused", "persian", "perspective", "pinch-zoom", "plus-darker", "plus-lighter", "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", "radial-gradient", "radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region", - "relative", "repeat", "repeating-linear-gradient", - "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse", + "relative", "repeat", "repeating-linear-gradient", "repeating-radial-gradient", + "repeating-conic-gradient", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY", "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running", - "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen", + "s-resize", "sans-serif", "saturate", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen", "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end", - "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", + "semi-condensed", "semi-expanded", "separate", "sepia", "serif", "show", "sidama", "simp-chinese-formal", "simp-chinese-informal", "single", "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal", "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali", "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square", - "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", - "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table", + "square-button", "start", "static", "status-bar", "stretch", "stroke", "stroke-box", "sub", + "subpixel-antialiased", "svg_masks", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row", "table-row-group", "tamil", @@ -677,10 +708,10 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", "trad-chinese-formal", "trad-chinese-informal", "transform", "translate", "translate3d", "translateX", "translateY", "translateZ", - "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up", + "transparent", "ultra-condensed", "ultra-expanded", "underline", "unidirectional-pan", "unset", "up", "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", - "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", + "var", "vertical", "vertical-text", "view-box", "visible", "visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor", "xx-large", "xx-small" @@ -748,7 +779,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { } }, ":": function(stream) { - if (stream.match(/\s*\{/, false)) + if (stream.match(/^\s*\{/, false)) return [null, null] return false; }, diff --git a/lib/redactor/codemirror/mode/css/gss.html b/lib/redactor/codemirror/mode/css/gss.html new file mode 100644 index 0000000..aa12bfb --- /dev/null +++ b/lib/redactor/codemirror/mode/css/gss.html @@ -0,0 +1,104 @@ + + +CodeMirror: Closure Stylesheets (GSS) mode + + + + + + + + + + + + + +
+

Closure Stylesheets (GSS) mode

+
+ + +

A mode for Closure Stylesheets (GSS).

+

MIME type defined: text/x-gss.

+ +

Parsing/Highlighting Tests: normal, verbose.

+ +
diff --git a/lib/redactor/codemirror/mode/css/gss_test.js b/lib/redactor/codemirror/mode/css/gss_test.js new file mode 100644 index 0000000..0901563 --- /dev/null +++ b/lib/redactor/codemirror/mode/css/gss_test.js @@ -0,0 +1,17 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + "use strict"; + + var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-gss"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss"); } + + MT("atComponent", + "[def @component] {", + "[tag foo] {", + " [property color]: [keyword black];", + "}", + "}"); + +})(); diff --git a/lib/redactor/codemirror/mode/css/index.html b/lib/redactor/codemirror/mode/css/index.html new file mode 100644 index 0000000..233d19b --- /dev/null +++ b/lib/redactor/codemirror/mode/css/index.html @@ -0,0 +1,81 @@ + + +CodeMirror: CSS mode + + + + + + + + + + + + +
+

CSS mode

+
+ + +

CSS mode supports this option:

+ +
highlightNonStandardPropertyKeywords: boolean
+
Whether to highlight non-standard CSS property keywords such as margin-inline or zoom (default: true).
+
+ +

MIME types defined: text/css, text/x-scss (demo), text/x-less (demo).

+ +

Parsing/Highlighting Tests: normal, verbose.

+ +
diff --git a/lib/redactor/codemirror/mode/css/less.html b/lib/redactor/codemirror/mode/css/less.html new file mode 100644 index 0000000..ea7db00 --- /dev/null +++ b/lib/redactor/codemirror/mode/css/less.html @@ -0,0 +1,152 @@ + + +CodeMirror: LESS mode + + + + + + + + + + +
+

LESS mode

+
+ + +

The LESS mode is a sub-mode of the CSS mode (defined in css.js).

+ +

Parsing/Highlighting Tests: normal, verbose.

+
diff --git a/lib/redactor/codemirror/mode/css/less_test.js b/lib/redactor/codemirror/mode/css/less_test.js new file mode 100644 index 0000000..cf367ea --- /dev/null +++ b/lib/redactor/codemirror/mode/css/less_test.js @@ -0,0 +1,54 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + "use strict"; + + var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-less"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "less"); } + + MT("variable", + "[variable-2 @base]: [atom #f04615];", + "[qualifier .class] {", + " [property width]: [variable&callee percentage]([number 0.5]); [comment // returns `50%`]", + " [property color]: [variable&callee saturate]([variable-2 @base], [number 5%]);", + "}"); + + MT("amp", + "[qualifier .child], [qualifier .sibling] {", + " [qualifier .parent] [atom &] {", + " [property color]: [keyword black];", + " }", + " [atom &] + [atom &] {", + " [property color]: [keyword red];", + " }", + "}"); + + MT("mixin", + "[qualifier .mixin] ([variable dark]; [variable-2 @color]) {", + " [property color]: [variable&callee darken]([variable-2 @color], [number 10%]);", + "}", + "[qualifier .mixin] ([variable light]; [variable-2 @color]) {", + " [property color]: [variable&callee lighten]([variable-2 @color], [number 10%]);", + "}", + "[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {", + " [property display]: [atom block];", + "}", + "[variable-2 @switch]: [variable light];", + "[qualifier .class] {", + " [qualifier .mixin]([variable-2 @switch]; [atom #888]);", + "}"); + + MT("nest", + "[qualifier .one] {", + " [def @media] ([property width]: [number 400px]) {", + " [property font-size]: [number 1.2em];", + " [def @media] [attribute print] [keyword and] [property color] {", + " [property color]: [keyword blue];", + " }", + " }", + "}"); + + + MT("interpolation", ".@{[variable foo]} { [property font-weight]: [atom bold]; }"); +})(); diff --git a/lib/redactor/codemirror/mode/css/scss.html b/lib/redactor/codemirror/mode/css/scss.html new file mode 100644 index 0000000..75ef4f9 --- /dev/null +++ b/lib/redactor/codemirror/mode/css/scss.html @@ -0,0 +1,158 @@ + + +CodeMirror: SCSS mode + + + + + + + + + + +
+

SCSS mode

+
+ + +

The SCSS mode is a sub-mode of the CSS mode (defined in css.js).

+ +

Parsing/Highlighting Tests: normal, verbose.

+ +
diff --git a/lib/redactor/codemirror/mode/css/scss_test.js b/lib/redactor/codemirror/mode/css/scss_test.js new file mode 100644 index 0000000..14c1d13 --- /dev/null +++ b/lib/redactor/codemirror/mode/css/scss_test.js @@ -0,0 +1,110 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } + + MT('url_with_quotation', + "[tag foo] { [property background]:[variable&callee url]([string test.jpg]) }"); + + MT('url_with_double_quotes', + "[tag foo] { [property background]:[variable&callee url]([string \"test.jpg\"]) }"); + + MT('url_with_single_quotes', + "[tag foo] { [property background]:[variable&callee url]([string \'test.jpg\']) }"); + + MT('string', + "[def @import] [string \"compass/css3\"]"); + + MT('important_keyword', + "[tag foo] { [property background]:[variable&callee url]([string \'test.jpg\']) [keyword !important] }"); + + MT('variable', + "[variable-2 $blue]:[atom #333]"); + + MT('variable_as_attribute', + "[tag foo] { [property color]:[variable-2 $blue] }"); + + MT('numbers', + "[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }"); + + MT('number_percentage', + "[tag foo] { [property width]:[number 80%] }"); + + MT('selector', + "[builtin #hello][qualifier .world]{}"); + + MT('singleline_comment', + "[comment // this is a comment]"); + + MT('multiline_comment', + "[comment /*foobar*/]"); + + MT('attribute_with_hyphen', + "[tag foo] { [property font-size]:[number 10px] }"); + + MT('string_after_attribute', + "[tag foo] { [property content]:[string \"::\"] }"); + + MT('directives', + "[def @include] [qualifier .mixin]"); + + MT('basic_structure', + "[tag p] { [property background]:[keyword red]; }"); + + MT('nested_structure', + "[tag p] { [tag a] { [property color]:[keyword red]; } }"); + + MT('mixin', + "[def @mixin] [tag table-base] {}"); + + MT('number_without_semicolon', + "[tag p] {[property width]:[number 12]}", + "[tag a] {[property color]:[keyword red];}"); + + MT('atom_in_nested_block', + "[tag p] { [tag a] { [property color]:[atom #000]; } }"); + + MT('interpolation_in_property', + "[tag foo] { #{[variable-2 $hello]}:[number 2]; }"); + + MT('interpolation_in_selector', + "[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }"); + + MT('interpolation_error', + "[tag foo]#{[variable foo]} { [property color]:[atom #000]; }"); + + MT("divide_operator", + "[tag foo] { [property width]:[number 4] [operator /] [number 2] }"); + + MT('nested_structure_with_id_selector', + "[tag p] { [builtin #hello] { [property color]:[keyword red]; } }"); + + MT('indent_mixin', + "[def @mixin] [tag container] (", + " [variable-2 $a]: [number 10],", + " [variable-2 $b]: [number 10])", + "{}"); + + MT('indent_nested', + "[tag foo] {", + " [tag bar] {", + " }", + "}"); + + MT('indent_parentheses', + "[tag foo] {", + " [property color]: [variable&callee darken]([variable-2 $blue],", + " [number 9%]);", + "}"); + + MT('indent_vardef', + "[variable-2 $name]:", + " [string 'val'];", + "[tag tag] {", + " [tag inner] {", + " [property margin]: [number 3px];", + " }", + "}"); +})(); diff --git a/lib/redactor/codemirror/mode/css/test.js b/lib/redactor/codemirror/mode/css/test.js new file mode 100644 index 0000000..573207e --- /dev/null +++ b/lib/redactor/codemirror/mode/css/test.js @@ -0,0 +1,217 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "css"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + // Error, because "foobarhello" is neither a known type or property, but + // property was expected (after "and"), and it should be in parentheses. + MT("atMediaUnknownType", + "[def @media] [attribute screen] [keyword and] [error foobarhello] { }"); + + // Soft error, because "foobarhello" is not a known property or type. + MT("atMediaUnknownProperty", + "[def @media] [attribute screen] [keyword and] ([error foobarhello]) { }"); + + // Make sure nesting works with media queries + MT("atMediaMaxWidthNested", + "[def @media] [attribute screen] [keyword and] ([property max-width]: [number 25px]) { [tag foo] { } }"); + + MT("atMediaFeatureValueKeyword", + "[def @media] ([property orientation]: [keyword landscape]) { }"); + + MT("atMediaUnknownFeatureValueKeyword", + "[def @media] ([property orientation]: [error upsidedown]) { }"); + + MT("atMediaUppercase", + "[def @MEDIA] ([property orienTAtion]: [keyword landScape]) { }"); + + MT("tagSelector", + "[tag foo] { }"); + + MT("classSelector", + "[qualifier .foo-bar_hello] { }"); + + MT("idSelector", + "[builtin #foo] { [error #foo] }"); + + MT("tagSelectorUnclosed", + "[tag foo] { [property margin]: [number 0] } [tag bar] { }"); + + MT("tagStringNoQuotes", + "[tag foo] { [property font-family]: [variable hello] [variable world]; }"); + + MT("tagStringDouble", + "[tag foo] { [property font-family]: [string \"hello world\"]; }"); + + MT("tagStringSingle", + "[tag foo] { [property font-family]: [string 'hello world']; }"); + + MT("tagColorKeyword", + "[tag foo] {", + " [property color]: [keyword black];", + " [property color]: [keyword navy];", + " [property color]: [keyword yellow];", + "}"); + + MT("tagColorHex3", + "[tag foo] { [property background]: [atom #fff]; }"); + + MT("tagColorHex4", + "[tag foo] { [property background]: [atom #ffff]; }"); + + MT("tagColorHex6", + "[tag foo] { [property background]: [atom #ffffff]; }"); + + MT("tagColorHex8", + "[tag foo] { [property background]: [atom #ffffffff]; }"); + + MT("tagColorHex5Invalid", + "[tag foo] { [property background]: [atom&error #fffff]; }"); + + MT("tagColorHexInvalid", + "[tag foo] { [property background]: [atom&error #ffg]; }"); + + MT("tagNegativeNumber", + "[tag foo] { [property margin]: [number -5px]; }"); + + MT("tagPositiveNumber", + "[tag foo] { [property padding]: [number 5px]; }"); + + MT("tagVendor", + "[tag foo] { [meta -foo-][property box-sizing]: [meta -foo-][atom border-box]; }"); + + MT("tagBogusProperty", + "[tag foo] { [property&error barhelloworld]: [number 0]; }"); + + MT("tagTwoProperties", + "[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }"); + + MT("tagTwoPropertiesURL", + "[tag foo] { [property background]: [variable&callee url]([string //example.com/foo.png]); [property padding]: [number 0]; }"); + + MT("indent_tagSelector", + "[tag strong], [tag em] {", + " [property background]: [variable&callee rgba](", + " [number 255], [number 255], [number 0], [number .2]", + " );", + "}"); + + MT("indent_atMedia", + "[def @media] {", + " [tag foo] {", + " [property color]:", + " [keyword yellow];", + " }", + "}"); + + MT("indent_comma", + "[tag foo] {", + " [property font-family]: [variable verdana],", + " [atom sans-serif];", + "}"); + + MT("indent_parentheses", + "[tag foo]:[variable-3 before] {", + " [property background]: [variable&callee url](", + "[string blahblah]", + "[string etc]", + "[string ]) [keyword !important];", + "}"); + + MT("font_face", + "[def @font-face] {", + " [property font-family]: [string 'myfont'];", + " [error nonsense]: [string 'abc'];", + " [property src]: [variable&callee url]([string http://blah]),", + " [variable&callee url]([string http://foo]);", + "}"); + + MT("empty_url", + "[def @import] [variable&callee url]() [attribute screen];"); + + MT("parens", + "[qualifier .foo] {", + " [property background-image]: [variable&callee fade]([atom #000], [number 20%]);", + " [property border-image]: [variable&callee linear-gradient](", + " [atom to] [atom bottom],", + " [variable&callee fade]([atom #000], [number 20%]) [number 0%],", + " [variable&callee fade]([atom #000], [number 20%]) [number 100%]", + " );", + "}"); + + MT("css_variable", + ":[variable-3 root] {", + " [variable-2 --main-color]: [atom #06c];", + "}", + "[tag h1][builtin #foo] {", + " [property color]: [variable&callee var]([variable-2 --main-color]);", + "}"); + + MT("blank_css_variable", + ":[variable-3 root] {", + " [variable-2 --]: [atom #06c];", + "}", + "[tag h1][builtin #foo] {", + " [property color]: [variable&callee var]([variable-2 --]);", + "}"); + + MT("supports", + "[def @supports] ([keyword not] (([property text-align-last]: [atom justify]) [keyword or] ([meta -moz-][property text-align-last]: [atom justify])) {", + " [property text-align-last]: [atom justify];", + "}"); + + MT("document", + "[def @document] [variable&callee url]([string http://blah]),", + " [variable&callee url-prefix]([string https://]),", + " [variable&callee domain]([string blah.com]),", + " [variable&callee regexp]([string \".*blah.+\"]) {", + " [builtin #id] {", + " [property background-color]: [keyword white];", + " }", + " [tag foo] {", + " [property font-family]: [variable Verdana], [atom sans-serif];", + " }", + "}"); + + MT("document_url", + "[def @document] [variable&callee url]([string http://blah]) { [qualifier .class] { } }"); + + MT("document_urlPrefix", + "[def @document] [variable&callee url-prefix]([string https://]) { [builtin #id] { } }"); + + MT("document_domain", + "[def @document] [variable&callee domain]([string blah.com]) { [tag foo] { } }"); + + MT("document_regexp", + "[def @document] [variable&callee regexp]([string \".*blah.+\"]) { [builtin #id] { } }"); + + MT("counter-style", + "[def @counter-style] [variable binary] {", + " [property system]: [atom numeric];", + " [property symbols]: [number 0] [number 1];", + " [property suffix]: [string \".\"];", + " [property range]: [atom infinite];", + " [property speak-as]: [atom numeric];", + "}"); + + MT("counter-style-additive-symbols", + "[def @counter-style] [variable simple-roman] {", + " [property system]: [atom additive];", + " [property additive-symbols]: [number 10] [variable X], [number 5] [variable V], [number 1] [variable I];", + " [property range]: [number 1] [number 49];", + "}"); + + MT("counter-style-use", + "[tag ol][qualifier .roman] { [property list-style]: [variable simple-roman]; }"); + + MT("counter-style-symbols", + "[tag ol] { [property list-style]: [variable&callee symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }"); + + MT("comment-does-not-disrupt", + "[def @font-face] [comment /* foo */] {", + " [property src]: [variable&callee url]([string x]);", + " [property font-family]: [variable One];", + "}") +})(); diff --git a/lib/redactor/codemirror/mode/htmlembedded/htmlembedded.js b/lib/redactor/codemirror/mode/htmlembedded/htmlembedded.js index 439e63a..5cceeef 100644 --- a/lib/redactor/codemirror/mode/htmlembedded/htmlembedded.js +++ b/lib/redactor/codemirror/mode/htmlembedded/htmlembedded.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS diff --git a/lib/redactor/codemirror/mode/htmlembedded/index.html b/lib/redactor/codemirror/mode/htmlembedded/index.html new file mode 100644 index 0000000..06e7717 --- /dev/null +++ b/lib/redactor/codemirror/mode/htmlembedded/index.html @@ -0,0 +1,60 @@ + + +CodeMirror: Html Embedded Scripts mode + + + + + + + + + + + + + + +
+

Html Embedded Scripts mode

+
+ + + +

Mode for html embedded scripts like JSP and ASP.NET. Depends on multiplex and HtmlMixed which in turn depends on + JavaScript, CSS and XML.
Other dependencies include those of the scripting language chosen.

+ +

MIME types defined: application/x-aspx (ASP.NET), + application/x-ejs (Embedded JavaScript), application/x-jsp (JavaServer Pages) + and application/x-erb

+
diff --git a/lib/redactor/codemirror/mode/htmlmixed/htmlmixed.js b/lib/redactor/codemirror/mode/htmlmixed/htmlmixed.js index 8341ac8..3f6d8b7 100644 --- a/lib/redactor/codemirror/mode/htmlmixed/htmlmixed.js +++ b/lib/redactor/codemirror/mode/htmlmixed/htmlmixed.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -50,7 +50,7 @@ } function getTagRegexp(tagName, anchored) { - return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i"); + return new RegExp((anchored ? "^" : "") + "<\/\\s*" + tagName + "\\s*>", "i"); } function addTags(from, to) { @@ -74,7 +74,8 @@ name: "xml", htmlMode: true, multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, - multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag + multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag, + allowMissingTagName: parserConfig.allowMissingTagName, }); var tags = {}; diff --git a/lib/redactor/codemirror/mode/htmlmixed/index.html b/lib/redactor/codemirror/mode/htmlmixed/index.html new file mode 100644 index 0000000..9548361 --- /dev/null +++ b/lib/redactor/codemirror/mode/htmlmixed/index.html @@ -0,0 +1,100 @@ + + +CodeMirror: HTML mixed mode + + + + + + + + + + + + + + +
+

HTML mixed mode

+
+ + +

The HTML mixed mode depends on the XML, JavaScript, and CSS modes.

+ +

It takes an optional mode configuration + option, tags, which can be used to add custom + behavior for specific tags. When given, it should be an object + mapping tag names (for example script) to arrays or + three-element arrays. Those inner arrays indicate [attributeName, + valueRegexp, modeSpec] + specifications. For example, you could use ["type", /^foo$/, + "foo"] to map the attribute type="foo" to + the foo mode. When the first two fields are null + ([null, null, "mode"]), the given mode is used for + any such tag that doesn't match any of the previously given + attributes. For example:

+ +
var myModeSpec = {
+  name: "htmlmixed",
+  tags: {
+    style: [["type", /^text\/(x-)?scss$/, "text/x-scss"],
+            [null, null, "css"]],
+    custom: [[null, null, "customMode"]]
+  }
+}
+ +

MIME types defined: text/html + (redefined, only takes effect if you load this parser after the + XML parser).

+ +
diff --git a/lib/redactor/codemirror/mode/javascript/index.html b/lib/redactor/codemirror/mode/javascript/index.html new file mode 100644 index 0000000..f4be5ea --- /dev/null +++ b/lib/redactor/codemirror/mode/javascript/index.html @@ -0,0 +1,118 @@ + + +CodeMirror: JavaScript mode + + + + + + + + + + + + +
+

JavaScript mode

+ + +
+ + + +

+ JavaScript mode supports several configuration options: +

    +
  • json which will set the mode to expect JSON + data rather than a JavaScript program.
  • +
  • jsonld which will set the mode to expect + JSON-LD linked data rather + than a JavaScript program (demo).
  • +
  • typescript which will activate additional + syntax highlighting and some other things for TypeScript code + (demo).
  • +
  • trackScope can be set to false to turn off + tracking of local variables. This will prevent locals from + getting the "variable-2" token type, and will + break completion of locals with javascript-hint.
  • +
  • statementIndent which (given a number) will + determine the amount of indentation to use for statements + continued on a new line.
  • +
  • wordCharacters, a regexp that indicates which + characters should be considered part of an identifier. + Defaults to /[\w$]/, which does not handle + non-ASCII identifiers. Can be set to something more elaborate + to improve Unicode support.
  • +
+

+ +

MIME types defined: text/javascript, application/javascript, application/x-javascript, text/ecmascript, application/ecmascript, application/json, application/x-json, application/manifest+json, application/ld+json, text/typescript, application/typescript.

+
diff --git a/lib/redactor/codemirror/mode/javascript/javascript.js b/lib/redactor/codemirror/mode/javascript/javascript.js index 16943a9..bb735eb 100644 --- a/lib/redactor/codemirror/mode/javascript/javascript.js +++ b/lib/redactor/codemirror/mode/javascript/javascript.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -16,6 +16,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var statementIndent = parserConfig.statementIndent; var jsonldMode = parserConfig.jsonld; var jsonMode = parserConfig.json || jsonldMode; + var trackScope = parserConfig.trackScope !== false var isTS = parserConfig.typescript; var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; @@ -98,21 +99,25 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (ch == "`") { state.tokenize = tokenQuasi; return tokenQuasi(stream, state); - } else if (ch == "#") { + } else if (ch == "#" && stream.peek() == "!") { stream.skipToEnd(); - return ret("error", "error"); - } else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) { + return ret("meta", "meta"); + } else if (ch == "#" && stream.eatWhile(wordRE)) { + return ret("variable", "property") + } else if (ch == "<" && stream.match("!--") || + (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) { stream.skipToEnd() return ret("comment", "comment") } else if (isOperatorChar.test(ch)) { if (ch != ">" || !state.lexical || state.lexical.type != ">") { if (stream.eat("=")) { if (ch == "!" || ch == "=") stream.eat("=") - } else if (/[<>*+\-]/.test(ch)) { + } else if (/[<>*+\-|&?]/.test(ch)) { stream.eat(ch) if (ch == ">") stream.eat(ch) } } + if (ch == "?" && stream.eat(".")) return ret(".") return ret("operator", "operator", stream.current()); } else if (wordRE.test(ch)) { stream.eatWhile(wordRE); @@ -122,7 +127,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var kw = keywords[word] return ret(kw.type, kw.style, word) } - if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false)) + if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false)) return ret("async", "keyword", word) } return ret("variable", "variable", word) @@ -214,7 +219,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { // Parser - var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, + "regexp": true, "this": true, "import": true, "jsonld-keyword": true}; function JSLexical(indented, column, type, align, prev, info) { this.indented = indented; @@ -226,6 +232,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function inScope(state, varname) { + if (!trackScope) return false for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; for (var cx = state.context; cx; cx = cx.prev) { @@ -272,6 +279,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function register(varname) { var state = cx.state; cx.marked = "def"; + if (!trackScope) return if (state.context) { if (state.lexical.info == "var" && state.context && state.context.block) { // FIXME function decls are also not block scoped @@ -322,6 +330,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { cx.state.context = new Context(cx.state.context, cx.state.localVars, true) cx.state.localVars = null } + pushcontext.lex = pushblockcontext.lex = true function popcontext() { cx.state.localVars = cx.state.context.vars cx.state.context = cx.state.context.prev @@ -371,7 +380,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); } if (type == "function") return cont(functiondef); - if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); + if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex); if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword" return cont(pushlex("form", type == "class" ? type : value), className, poplex) @@ -417,7 +426,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function parenExpr(type) { if (type != "(") return pass() - return cont(pushlex(")"), expression, expect(")"), poplex) + return cont(pushlex(")"), maybeexpression, expect(")"), poplex) } function expressionInner(type, value, noComma) { if (cx.state.fatArrowAt == cx.stream.start) { @@ -437,7 +446,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "{") return contCommasep(objprop, "}", null, maybeop); if (type == "quasi") return pass(quasi, maybeop); if (type == "new") return cont(maybeTarget(noComma)); - if (type == "import") return cont(expression); return cont(); } function maybeexpression(type) { @@ -446,7 +454,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function maybeoperatorComma(type, value) { - if (type == ",") return cont(expression); + if (type == ",") return cont(maybeexpression); return maybeoperatorNoComma(type, value, false); } function maybeoperatorNoComma(type, value, noComma) { @@ -455,7 +463,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); - if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false)) + if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false)) return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); if (value == "?") return cont(expression, expect(":"), expr); return cont(expr); @@ -475,7 +483,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function quasi(type, value) { if (type != "quasi") return pass(); if (value.slice(value.length - 2) != "${") return cont(quasi); - return cont(expression, continueQuasi); + return cont(maybeexpression, continueQuasi); } function continueQuasi(type) { if (type == "}") { @@ -601,7 +609,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } } function typeexpr(type, value) { - if (value == "keyof" || value == "typeof" || value == "infer") { + if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") { cx.marked = "keyword" return cont(value == "typeof" ? expressionNoComma : typeexpr) } @@ -612,13 +620,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "|" || value == "&") return cont(typeexpr) if (type == "string" || type == "number" || type == "atom") return cont(afterType); if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) - if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) + if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) + if (type == "quasi") { return pass(quasiType, afterType); } } function maybeReturnType(type) { if (type == "=>") return cont(typeexpr) } + function typeprops(type) { + if (type.match(/[\}\)\]]/)) return cont() + if (type == "," || type == ";") return cont(typeprops) + return pass(typeprop, typeprops) + } function typeprop(type, value) { if (type == "variable" || cx.style == "keyword") { cx.marked = "property" @@ -631,6 +645,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) } else if (type == "(") { return pass(functiondecl, typeprop) + } else if (!type.match(/[;\}\)\],]/)) { + return cont() + } + } + function quasiType(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasiType); + return cont(typeexpr, continueQuasiType); + } + function continueQuasiType(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasiType); } } function typearg(type, value) { @@ -751,17 +779,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "async" || (type == "variable" && (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && - cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) { + cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/, false))) { cx.marked = "keyword"; return cont(classBody); } if (type == "variable" || cx.style == "keyword") { cx.marked = "property"; - return cont(isTS ? classfield : functiondef, classBody); + return cont(classfield, classBody); } - if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody); + if (type == "number" || type == "string") return cont(classfield, classBody); if (type == "[") - return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody) + return cont(expression, maybetype, expect("]"), classfield, classBody) if (value == "*") { cx.marked = "keyword"; return cont(classBody); @@ -772,6 +800,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "@") return cont(expression, classBody) } function classfield(type, value) { + if (value == "!") return cont(classfield) if (value == "?") return cont(classfield) if (type == ":") return cont(typeexpr, maybeAssign) if (value == "=") return cont(expressionNoComma) @@ -791,6 +820,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function afterImport(type) { if (type == "string") return cont(); if (type == "(") return pass(expression); + if (type == ".") return pass(maybeoperatorComma); return pass(importSpec, maybeMoreImports, maybeFrom); } function importSpec(type, value) { @@ -864,14 +894,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { }, indent: function(state, textAfter) { - if (state.tokenize == tokenComment) return CodeMirror.Pass; + if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass; if (state.tokenize != tokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top // Kludge to prevent 'maybelse' from blocking lexical scope pops if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { var c = state.cc[i]; if (c == poplex) lexical = lexical.prev; - else if (c != maybeelse) break; + else if (c != maybeelse && c != popcontext) break; } while ((lexical.type == "stat" || lexical.type == "form") && (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && @@ -908,8 +938,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { expressionAllowed: expressionAllowed, skipExpression: function(state) { - var top = state.cc[state.cc.length - 1] - if (top == expression || top == expressionNoComma) state.cc.pop() + parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null)) } }; }); @@ -921,9 +950,10 @@ CodeMirror.defineMIME("text/ecmascript", "javascript"); CodeMirror.defineMIME("application/javascript", "javascript"); CodeMirror.defineMIME("application/x-javascript", "javascript"); CodeMirror.defineMIME("application/ecmascript", "javascript"); -CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); -CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); -CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); +CodeMirror.defineMIME("application/json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true }); +CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true }) +CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true }); CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/lib/redactor/codemirror/mode/javascript/json-ld.html b/lib/redactor/codemirror/mode/javascript/json-ld.html new file mode 100644 index 0000000..d3dff2e --- /dev/null +++ b/lib/redactor/codemirror/mode/javascript/json-ld.html @@ -0,0 +1,72 @@ + + +CodeMirror: JSON-LD mode + + + + + + + + + + + + +
+

JSON-LD mode

+ + +
+ + + +

This is a specialization of the JavaScript mode.

+
diff --git a/lib/redactor/codemirror/mode/javascript/test.js b/lib/redactor/codemirror/mode/javascript/test.js new file mode 100644 index 0000000..a1bcf1f --- /dev/null +++ b/lib/redactor/codemirror/mode/javascript/test.js @@ -0,0 +1,521 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "javascript"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT("locals", + "[keyword function] [def foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }"); + + MT("comma-and-binop", + "[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }"); + + MT("destructuring", + "([keyword function]([def a], [[[def b], [def c] ]]) {", + " [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);", + " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];", + "})();"); + + MT("destructure_trailing_comma", + "[keyword let] {[def a], [def b],} [operator =] [variable foo];", + "[keyword let] [def c];"); // Parser still in good state? + + MT("class_body", + "[keyword class] [def Foo] {", + " [property constructor]() {}", + " [property sayName]() {", + " [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];", + " }", + "}"); + + MT("class", + "[keyword class] [def Point] [keyword extends] [variable SuperThing] {", + " [keyword get] [property prop]() { [keyword return] [number 24]; }", + " [property constructor]([def x], [def y]) {", + " [keyword super]([string 'something']);", + " [keyword this].[property x] [operator =] [variable-2 x];", + " }", + "}"); + + MT("anonymous_class_expression", + "[keyword const] [def Adder] [operator =] [keyword class] [keyword extends] [variable Arithmetic] {", + " [property add]([def a], [def b]) {}", + "};"); + + MT("named_class_expression", + "[keyword const] [def Subber] [operator =] [keyword class] [def Subtract] {", + " [property sub]([def a], [def b]) {}", + "};"); + + MT("class_async_method", + "[keyword class] [def Foo] {", + " [property sayName1]() {}", + " [keyword async] [property sayName2]() {}", + "}"); + + MT("import", + "[keyword function] [def foo]() {", + " [keyword import] [def $] [keyword from] [string 'jquery'];", + " [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];", + "}"); + + MT("import_trailing_comma", + "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']") + + MT("import_dynamic", + "[keyword import]([string 'baz']).[property then]") + + MT("import_dynamic", + "[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]") + + MT("const", + "[keyword function] [def f]() {", + " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];", + "}"); + + MT("for/of", + "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}"); + + MT("for await", + "[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}"); + + MT("generator", + "[keyword function*] [def repeat]([def n]) {", + " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])", + " [keyword yield] [variable-2 i];", + "}"); + + MT("let_scoping", + "[keyword function] [def scoped]([def n]) {", + " { [keyword var] [def i]; } [variable-2 i];", + " { [keyword let] [def j]; [variable-2 j]; } [variable j];", + " [keyword if] ([atom true]) { [keyword const] [def k]; [variable-2 k]; } [variable k];", + "}"); + + MT("switch_scoping", + "[keyword switch] ([variable x]) {", + " [keyword default]:", + " [keyword let] [def j];", + " [keyword return] [variable-2 j]", + "}", + "[variable j];") + + MT("leaving_scope", + "[keyword function] [def a]() {", + " {", + " [keyword const] [def x] [operator =] [number 1]", + " [keyword if] ([atom true]) {", + " [keyword let] [def y] [operator =] [number 2]", + " [keyword var] [def z] [operator =] [number 3]", + " [variable console].[property log]([variable-2 x], [variable-2 y], [variable-2 z])", + " }", + " [variable console].[property log]([variable-2 x], [variable y], [variable-2 z])", + " }", + " [variable console].[property log]([variable x], [variable y], [variable-2 z])", + "}") + + MT("quotedStringAddition", + "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];"); + + MT("quotedFatArrow", + "[keyword let] [def f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];"); + + MT("fatArrow", + "[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);", + "[variable a];", // No longer in scope + "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];", + "[variable c];"); + + MT("fatArrow_stringDefault", + "([def a], [def b] [operator =] [string 'x\\'y']) [operator =>] [variable-2 a] [operator +] [variable-2 b]") + + MT("spread", + "[keyword function] [def f]([def a], [meta ...][def b]) {", + " [variable something]([variable-2 a], [meta ...][variable-2 b]);", + "}"); + + MT("quasi", + "[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]"); + + MT("quasi_no_function", + "[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]"); + + MT("indent_statement", + "[keyword var] [def x] [operator =] [number 10]", + "[variable x] [operator +=] [variable y] [operator +]", + " [atom Infinity]", + "[keyword debugger];"); + + MT("indent_if", + "[keyword if] ([number 1])", + " [keyword break];", + "[keyword else] [keyword if] ([number 2])", + " [keyword continue];", + "[keyword else]", + " [number 10];", + "[keyword if] ([number 1]) {", + " [keyword break];", + "} [keyword else] [keyword if] ([number 2]) {", + " [keyword continue];", + "} [keyword else] {", + " [number 10];", + "}"); + + MT("indent_for", + "[keyword for] ([keyword var] [def i] [operator =] [number 0];", + " [variable-2 i] [operator <] [number 100];", + " [variable-2 i][operator ++])", + " [variable doSomething]([variable-2 i]);", + "[keyword debugger];"); + + MT("indent_c_style", + "[keyword function] [def foo]()", + "{", + " [keyword debugger];", + "}"); + + MT("indent_else", + "[keyword for] (;;)", + " [keyword if] ([variable foo])", + " [keyword if] ([variable bar])", + " [number 1];", + " [keyword else]", + " [number 2];", + " [keyword else]", + " [number 3];"); + + MT("indent_funarg", + "[variable foo]([number 10000],", + " [keyword function]([def a]) {", + " [keyword debugger];", + "};"); + + MT("indent_below_if", + "[keyword for] (;;)", + " [keyword if] ([variable foo])", + " [number 1];", + "[number 2];"); + + MT("indent_semicolonless_if", + "[keyword function] [def foo]() {", + " [keyword if] ([variable x])", + " [variable foo]()", + "}") + + MT("indent_semicolonless_if_with_statement", + "[keyword function] [def foo]() {", + " [keyword if] ([variable x])", + " [variable foo]()", + " [variable bar]()", + "}") + + MT("multilinestring", + "[keyword var] [def x] [operator =] [string 'foo\\]", + "[string bar'];"); + + MT("scary_regexp", + "[string-2 /foo[[/]]bar/];"); + + MT("indent_strange_array", + "[keyword var] [def x] [operator =] [[", + " [number 1],,", + " [number 2],", + "]];", + "[number 10];"); + + MT("param_default", + "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {", + " [keyword return] [variable-2 x];", + "}"); + + MT( + "param_destructuring", + "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {", + " [keyword return] [variable-2 x];", + "}"); + + MT("new_target", + "[keyword function] [def F]([def target]) {", + " [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {", + " [keyword return] [keyword new]", + " .[keyword target];", + " }", + "}"); + + MT("async", + "[keyword async] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }"); + + MT("async_assignment", + "[keyword const] [def foo] [operator =] [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; };"); + + MT("async_object", + "[keyword let] [def obj] [operator =] { [property async]: [atom false] };"); + + // async be highlighted as keyword and foo as def, but it requires potentially expensive look-ahead. See #4173 + MT("async_object_function", + "[keyword let] [def obj] [operator =] { [property async] [property foo]([def args]) { [keyword return] [atom true]; } };"); + + MT("async_object_properties", + "[keyword let] [def obj] [operator =] {", + " [property prop1]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },", + " [property prop2]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },", + " [property prop3]: [keyword async] [keyword function] [def prop3]([def args]) { [keyword return] [atom true]; },", + "};"); + + MT("async_arrow", + "[keyword const] [def foo] [operator =] [keyword async] ([def args]) [operator =>] { [keyword return] [atom true]; };"); + + MT("async_jquery", + "[variable $].[property ajax]({", + " [property url]: [variable url],", + " [property async]: [atom true],", + " [property method]: [string 'GET']", + "});"); + + MT("async_variable", + "[keyword const] [def async] [operator =] {[property a]: [number 1]};", + "[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];") + + MT("bigint", "[number 1n] [operator +] [number 0x1afn] [operator +] [number 0o064n] [operator +] [number 0b100n];") + + MT("async_comment", + "[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }"); + + MT("indent_switch", + "[keyword switch] ([variable x]) {", + " [keyword default]:", + " [keyword return] [number 2]", + "}") + + MT("regexp_corner_case", + "[operator +]{} [operator /] [atom undefined];", + "[[[meta ...][string-2 /\\//] ]];", + "[keyword void] [string-2 /\\//];", + "[keyword do] [string-2 /\\//]; [keyword while] ([number 0]);", + "[keyword if] ([number 0]) {} [keyword else] [string-2 /\\//];", + "[string-2 `${][variable async][operator ++][string-2 }//`];", + "[string-2 `${]{} [operator /] [string-2 /\\//}`];") + + MT("return_eol", + "[keyword return]", + "{} [string-2 /5/]") + + MT("numeric separator", + "[number 123_456];", + "[number 0xdead_c0de];", + "[number 0o123_456];", + "[number 0b1101_1101];", + "[number .123_456e0_1];", + "[number 1E+123_456];", + "[number 12_34_56n];") + + MT("underscore property", + "[variable something].[property _property];", + "[variable something].[property _123];", + "[variable something].[property _for];", + "[variable _for];", + "[variable _123];") + + MT("private properties", + "[keyword class] [def C] {", + " [property #x] [operator =] [number 2];", + " [property #read]() {", + " [keyword return] [keyword this].[property #x]", + " }", + "}") + + var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript") + function TS(name) { + test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) + } + + TS("typescript_extend_type", + "[keyword class] [def Foo] [keyword extends] [type Some][operator <][type Type][operator >] {}") + + TS("typescript_arrow_type", + "[keyword let] [def x]: ([variable arg]: [type Type]) [operator =>] [type ReturnType]") + + TS("typescript_class", + "[keyword class] [def Foo] {", + " [keyword public] [keyword static] [property main]() {}", + " [keyword private] [property _foo]: [type string];", + "}") + + TS("typescript_literal_types", + "[keyword import] [keyword *] [keyword as] [def Sequelize] [keyword from] [string 'sequelize'];", + "[keyword interface] [def MyAttributes] {", + " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];", + " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];", + "}", + "[keyword interface] [def MyInstance] [keyword extends] [type Sequelize].[type Instance] [operator <] [type MyAttributes] [operator >] {", + " [property rawAttributes]: [type MyAttributes];", + " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];", + " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];", + "}") + + TS("typescript_extend_operators", + "[keyword export] [keyword interface] [def UserModel] [keyword extends]", + " [type Sequelize].[type Model] [operator <] [type UserInstance], [type UserAttributes] [operator >] {", + " [property findById]: (", + " [variable userId]: [type number]", + " ) [operator =>] [type Promise] [operator <] [type Array] [operator <] { [property id], [property name] } [operator >>];", + " [property updateById]: (", + " [variable userId]: [type number],", + " [variable isActive]: [type boolean]", + " ) [operator =>] [type Promise] [operator <] [type AccountHolderNotificationPreferenceInstance] [operator >];", + " }") + + TS("typescript_interface_with_const", + "[keyword const] [def hello]: {", + " [property prop1][operator ?]: [type string];", + " [property prop2][operator ?]: [type string];", + "} [operator =] {};") + + TS("typescript_double_extend", + "[keyword export] [keyword interface] [def UserAttributes] {", + " [property id][operator ?]: [type number];", + " [property createdAt][operator ?]: [type Date];", + "}", + "[keyword export] [keyword interface] [def UserInstance] [keyword extends] [type Sequelize].[type Instance][operator <][type UserAttributes][operator >], [type UserAttributes] {", + " [property id]: [type number];", + " [property createdAt]: [type Date];", + "}"); + + TS("typescript_index_signature", + "[keyword interface] [def A] {", + " [[ [variable prop]: [type string] ]]: [type any];", + " [property prop1]: [type any];", + "}"); + + TS("typescript_generic_class", + "[keyword class] [def Foo][operator <][type T][operator >] {", + " [property bar]() {}", + " [property foo](): [type Foo] {}", + "}") + + TS("typescript_type_when_keyword", + "[keyword export] [keyword type] [type AB] [operator =] [type A] [operator |] [type B];", + "[keyword type] [type Flags] [operator =] {", + " [property p1]: [type string];", + " [property p2]: [type boolean];", + "};") + + TS("typescript_type_when_not_keyword", + "[keyword class] [def HasType] {", + " [property type]: [type string];", + " [property constructor]([def type]: [type string]) {", + " [keyword this].[property type] [operator =] [variable-2 type];", + " }", + " [property setType]({ [def type] }: { [property type]: [type string]; }) {", + " [keyword this].[property type] [operator =] [variable-2 type];", + " }", + "}") + + TS("typescript_function_generics", + "[keyword function] [def a]() {}", + "[keyword function] [def b][operator <][type IA] [keyword extends] [type object], [type IB] [keyword extends] [type object][operator >]() {}", + "[keyword function] [def c]() {}") + + TS("typescript_complex_return_type", + "[keyword function] [def A]() {", + " [keyword return] [keyword this].[property property];", + "}", + "[keyword function] [def B](): [type Promise][operator <]{ [[ [variable key]: [type string] ]]: [type any] } [operator |] [atom null][operator >] {", + " [keyword return] [keyword this].[property property];", + "}") + + TS("typescript_complex_type_casting", + "[keyword const] [def giftpay] [operator =] [variable config].[property get]([string 'giftpay']) [keyword as] { [[ [variable platformUuid]: [type string] ]]: { [property version]: [type number]; [property apiCode]: [type string]; } };") + + TS("typescript_keyof", + "[keyword function] [def x][operator <][type T] [keyword extends] [keyword keyof] [type X][operator >]([def a]: [type T]) {", + " [keyword return]") + + TS("typescript_new_typeargs", + "[keyword let] [def x] [operator =] [keyword new] [variable Map][operator <][type string], [type Date][operator >]([string-2 `foo${][variable bar][string-2 }`])") + + TS("modifiers", + "[keyword class] [def Foo] {", + " [keyword public] [keyword abstract] [property bar]() {}", + " [property constructor]([keyword readonly] [keyword private] [def x]) {}", + "}") + + TS("arrow prop", + "({[property a]: [def p] [operator =>] [variable-2 p]})") + + TS("generic in function call", + "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);", + "[keyword this].[property a][operator <][variable Type][operator >][variable foo];") + + TS("type guard", + "[keyword class] [def Appler] {", + " [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {", + " [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))", + " [keyword throw] [keyword new] [variable Error]();", + " }", + "}") + + TS("type as variable", + "[variable type] [operator =] [variable x] [keyword as] [type Bar];"); + + TS("enum body", + "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {", + " [def ERROR] [operator =] [string 'problem_type_error'],", + " [def WARNING] [operator =] [string 'problem_type_warning'],", + " [def META],", + "}") + + TS("parenthesized type", + "[keyword class] [def Foo] {", + " [property x] [operator =] [keyword new] [variable A][operator <][type B], [type string][operator |](() [operator =>] [type void])[operator >]();", + " [keyword private] [property bar]();", + "}") + + TS("abstract class", + "[keyword export] [keyword abstract] [keyword class] [def Foo] {}") + + TS("interface without semicolons", + "[keyword interface] [def Foo] {", + " [property greet]([def x]: [type int]): [type blah]", + " [property bar]: [type void]", + "}") + + var jsonld_mode = CodeMirror.getMode( + {indentUnit: 2}, + {name: "javascript", jsonld: true} + ); + function LD(name) { + test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1)); + } + + LD("json_ld_keywords", + '{', + ' [meta "@context"]: {', + ' [meta "@base"]: [string "http://example.com"],', + ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],', + ' [property "likesFlavor"]: {', + ' [meta "@container"]: [meta "@list"]', + ' [meta "@reverse"]: [string "@beFavoriteOf"]', + ' },', + ' [property "nick"]: { [meta "@container"]: [meta "@set"] },', + ' [property "nick"]: { [meta "@container"]: [meta "@index"] }', + ' },', + ' [meta "@graph"]: [[ {', + ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],', + ' [property "name"]: [string "John Lennon"],', + ' [property "modified"]: {', + ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],', + ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]', + ' }', + ' } ]]', + '}'); + + LD("json_ld_fake", + '{', + ' [property "@fake"]: [string "@fake"],', + ' [property "@contextual"]: [string "@identifier"],', + ' [property "user@domain.com"]: [string "@graphical"],', + ' [property "@ID"]: [string "@@ID"]', + '}'); +})(); diff --git a/lib/redactor/codemirror/mode/javascript/typescript.html b/lib/redactor/codemirror/mode/javascript/typescript.html new file mode 100644 index 0000000..d0bc3ae --- /dev/null +++ b/lib/redactor/codemirror/mode/javascript/typescript.html @@ -0,0 +1,62 @@ + + +CodeMirror: TypeScript mode + + + + + + + + + + +
+

TypeScript mode

+ + +
+ + + +

This is a specialization of the JavaScript mode.

+
diff --git a/lib/redactor/codemirror/mode/meta.js b/lib/redactor/codemirror/mode/meta.js index d44590f..2d52ba6 100644 --- a/lib/redactor/codemirror/mode/meta.js +++ b/lib/redactor/codemirror/mode/meta.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -19,12 +19,12 @@ {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]}, {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h", "ino"]}, {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]}, - {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, + {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy", "cbl"]}, {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp", "cs"]}, {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]}, {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]}, {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]}, - {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/}, + {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists\.txt$/}, {name: "CoffeeScript", mimes: ["application/vnd.coffeescript", "text/coffeescript", "text/x-coffeescript"], mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]}, @@ -44,7 +44,7 @@ {name: "edn", mime: "application/edn", mode: "clojure", ext: ["edn"]}, {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]}, {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]}, - {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]}, + {name: "Embedded JavaScript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]}, {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]}, {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]}, {name: "Esper", mime: "text/x-esper", mode: "sql"}, @@ -55,7 +55,7 @@ {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]}, {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]}, {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]}, - {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i}, + {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history)\.md$/i}, {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]}, {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"], file: /^Jenkinsfile$/}, {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]}, @@ -76,7 +76,7 @@ {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]}, {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]}, {name: "Jinja2", mime: "text/jinja2", mode: "jinja2", ext: ["j2", "jinja", "jinja2"]}, - {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, + {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"], alias: ["jl"]}, {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]}, {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]}, {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]}, @@ -144,7 +144,7 @@ {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v", "sv", "svh"]}, {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]}, {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]}, - {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, + {name: "TiddlyWiki", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"}, {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]}, {name: "Tornado", mime: "text/x-tornado", mode: "tornado"}, @@ -169,7 +169,8 @@ {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}, {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]}, {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]}, - {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]} + {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]}, + {name: "WebAssembly", mime: "text/webassembly", mode: "wast", ext: ["wat", "wast"]}, ]; // Ensure all modes have a mime property for backwards compatibility for (var i = 0; i < CodeMirror.modeInfo.length; i++) { diff --git a/lib/redactor/codemirror/mode/php/index.html b/lib/redactor/codemirror/mode/php/index.html new file mode 100644 index 0000000..d4439fd --- /dev/null +++ b/lib/redactor/codemirror/mode/php/index.html @@ -0,0 +1,64 @@ + + +CodeMirror: PHP mode + + + + + + + + + + + + + + + +
+

PHP mode

+
+ + + +

Simple HTML/PHP mode based on + the C-like mode. Depends on XML, + JavaScript, CSS, HTMLMixed, and C-like modes.

+ +

MIME types defined: application/x-httpd-php (HTML with PHP code), text/x-php (plain, non-wrapped PHP code).

+
diff --git a/lib/redactor/codemirror/mode/php/php.js b/lib/redactor/codemirror/mode/php/php.js index 5f3a143..281c290 100644 --- a/lib/redactor/codemirror/mode/php/php.js +++ b/lib/redactor/codemirror/mode/php/php.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -53,7 +53,7 @@ [["]", null]] ], closing, escapes); } - if (stream.match(/\-\>\w/, false)) { + if (stream.match(/^->\w/, false)) { // Match object operator state.tokenize = matchSequence([ [["->", null]], @@ -80,13 +80,13 @@ } var phpKeywords = "abstract and array as break case catch class clone const continue declare default " + - "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " + + "do else elseif enddeclare endfor endforeach endif endswitch endwhile enum extends final " + "for foreach function global goto if implements interface instanceof namespace " + "new or private protected public static switch throw trait try use var while xor " + "die echo empty exit eval include include_once isset list require require_once return " + - "print unset __halt_compiler self static parent yield insteadof finally"; + "print unset __halt_compiler self static parent yield insteadof finally readonly match"; var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__"; - var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count"; + var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage memory_get_peak_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count"; CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" ")); CodeMirror.registerHelper("wordChars", "php", /[\w$]/); @@ -95,7 +95,7 @@ helperType: "php", keywords: keywords(phpKeywords), blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"), - defKeywords: keywords("class function interface namespace trait"), + defKeywords: keywords("class enum function interface namespace trait"), atoms: keywords(phpAtoms), builtin: keywords(phpBuiltin), multiLineStrings: true, @@ -106,7 +106,7 @@ }, "<": function(stream, state) { var before; - if (before = stream.match(/<<\s*/)) { + if (before = stream.match(/^<<\s*/)) { var quoted = stream.eat(/['"]/); stream.eatWhile(/[\w\.]/); var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1)); diff --git a/lib/redactor/codemirror/mode/php/test.js b/lib/redactor/codemirror/mode/php/test.js new file mode 100644 index 0000000..c4c06c4 --- /dev/null +++ b/lib/redactor/codemirror/mode/php/test.js @@ -0,0 +1,154 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "php"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT('simple_test', + '[meta ]'); + + MT('variable_interpolation_non_alphanumeric', + '[meta $/$\\$}$\\\"$:$;$?$|$[[$]]$+$=aaa"]', + '[meta ?>]'); + + MT('variable_interpolation_digits', + '[meta ]'); + + MT('variable_interpolation_simple_syntax_1', + '[meta ]'); + + MT('variable_interpolation_simple_syntax_2', + '[meta ]'); + + MT('variable_interpolation_simple_syntax_3', + '[meta [variable aaaaa][string .aaaaaa"];', + '[keyword echo] [string "aaa][variable-2 $aaaa][string ->][variable-2 $aaaaa][string .aaaaaa"];', + '[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string [[2]].aaaaaa"];', + '[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string ->aaaa2.aaaaaa"];', + '[meta ?>]'); + + MT('variable_interpolation_escaping', + '[meta aaa.aaa"];', + '[keyword echo] [string "aaa\\$aaaa[[2]]aaa.aaa"];', + '[keyword echo] [string "aaa\\$aaaa[[asd]]aaa.aaa"];', + '[keyword echo] [string "aaa{\\$aaaa->aaa.aaa"];', + '[keyword echo] [string "aaa{\\$aaaa[[2]]aaa.aaa"];', + '[keyword echo] [string "aaa{\\aaaaa[[asd]]aaa.aaa"];', + '[keyword echo] [string "aaa\\${aaaa->aaa.aaa"];', + '[keyword echo] [string "aaa\\${aaaa[[2]]aaa.aaa"];', + '[keyword echo] [string "aaa\\${aaaa[[asd]]aaa.aaa"];', + '[meta ?>]'); + + MT('variable_interpolation_complex_syntax_1', + '[meta aaa.aaa"];', + '[keyword echo] [string "aaa][variable-2 $]{[variable-2 $aaaa]}[string ->aaa.aaa"];', + '[keyword echo] [string "aaa][variable-2 $]{[variable-2 $aaaa][[',' [number 42]',']]}[string ->aaa.aaa"];', + '[keyword echo] [string "aaa][variable-2 $]{[variable aaaa][meta ?>]aaaaaa'); + + MT('variable_interpolation_complex_syntax_2', + '[meta } $aaaaaa.aaa"];', + '[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*}?>*/][[',' [string "aaa][variable-2 $aaa][string {}][variable-2 $]{[variable aaa]}[string "]',']]}[string ->aaa.aaa"];', + '[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*} } $aaa } */]}[string ->aaa.aaa"];'); + + + function build_recursive_monsters(nt, t, n){ + var monsters = [t]; + for (var i = 1; i <= n; ++i) + monsters[i] = nt.join(monsters[i - 1]); + return monsters; + } + + var m1 = build_recursive_monsters( + ['[string "][variable-2 $]{[variable aaa] [operator +] ', '}[string "]'], + '[comment /* }?>} */] [string "aaa][variable-2 $aaa][string .aaa"]', + 10 + ); + + MT('variable_interpolation_complex_syntax_3_1', + '[meta ]'); + + var m2 = build_recursive_monsters( + ['[string "a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', '}[string .a"]'], + '[comment /* }?>{{ */] [string "a?>}{{aa][variable-2 $aaa][string .a}a?>a"]', + 5 + ); + + MT('variable_interpolation_complex_syntax_3_2', + '[meta ]'); + + function build_recursive_monsters_2(mf1, mf2, nt, t, n){ + var monsters = [t]; + for (var i = 1; i <= n; ++i) + monsters[i] = nt[0] + mf1[i - 1] + nt[1] + mf2[i - 1] + nt[2] + monsters[i - 1] + nt[3]; + return monsters; + } + + var m3 = build_recursive_monsters_2( + m1, + m2, + ['[string "a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', ' [operator +] ', '}[string .a"]'], + '[comment /* }?>{{ */] [string "a?>}{{aa][variable-2 $aaa][string .a}a?>a"]', + 4 + ); + + MT('variable_interpolation_complex_syntax_3_3', + '[meta ]'); + + MT("variable_interpolation_heredoc", + "[meta + +CodeMirror: Smarty mode + + + + + + + + + + +
+

Smarty mode

+
+ +

Mode for Smarty version 2 or 3, which allows for custom delimiter tags.

+ +

Several configuration parameters are supported:

+ +
    +
  • leftDelimiter and rightDelimiter, + which should be strings that determine where the Smarty syntax + starts and ends.
  • +
  • version, which should be 2 or 3.
  • +
  • baseMode, which can be a mode spec + like "text/html" to set a different background mode.
  • +
+ +

MIME types defined: text/x-smarty

+ +

Smarty 2, custom delimiters

+ +
+ +

Smarty 3

+ + + + + +
diff --git a/lib/redactor/codemirror/mode/smarty/smarty.js b/lib/redactor/codemirror/mode/smarty/smarty.js index 57852fe..4660b9b 100644 --- a/lib/redactor/codemirror/mode/smarty/smarty.js +++ b/lib/redactor/codemirror/mode/smarty/smarty.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE /** * Smarty 2 and 3 mode. diff --git a/lib/redactor/codemirror/mode/sql/index.html b/lib/redactor/codemirror/mode/sql/index.html new file mode 100644 index 0000000..2b61145 --- /dev/null +++ b/lib/redactor/codemirror/mode/sql/index.html @@ -0,0 +1,92 @@ + + +CodeMirror: SQL Mode for CodeMirror + + + + + + + + + + + + + +
+

SQL Mode for CodeMirror

+
+ +
+

MIME types defined: + text/x-sql, + text/x-mysql, + text/x-mariadb, + text/x-cassandra, + text/x-plsql, + text/x-mssql, + text/x-hive, + text/x-pgsql, + text/x-gql, + text/x-gpsql. + text/x-esper. + text/x-sqlite. + text/x-sparksql. + text/x-trino. +

+ + +
diff --git a/lib/redactor/codemirror/mode/sql/sql.js b/lib/redactor/codemirror/mode/sql/sql.js index e43495d..a386f5c 100644 --- a/lib/redactor/codemirror/mode/sql/sql.js +++ b/lib/redactor/codemirror/mode/sql/sql.js @@ -1,5 +1,5 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE +// Distributed under an MIT license: https://codemirror.net/5/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -35,19 +35,19 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { if (support.hexNumber && ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) - || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { + || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]*'/))) { // hex - // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/hexadecimal-literals.html return "number"; } else if (support.binaryNumber && - (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) + (((ch == "b" || ch == "B") && stream.match(/^'[01]*'/)) || (ch == "0" && stream.match(/^b[01]+/)))) { // bitstring - // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/bit-value-literals.html return "number"; } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { // numbers - // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/number-literals.html stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/); support.decimallessFloat && stream.match(/^\.(?!\.)/); return "number"; @@ -56,14 +56,14 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { return "variable-3"; } else if (ch == "'" || (ch == '"' && support.doubleQuote)) { // strings - // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html state.tokenize = tokenLiteral(ch); return state.tokenize(stream, state); } else if ((((support.nCharCast && (ch == "n" || ch == "N")) || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) && (stream.peek() == "'" || stream.peek() == '"'))) { // charset casting: _utf8'str', N'str', n'str' - // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html return "keyword"; } else if (support.escapeConstant && (ch == "e" || ch == "E") && (stream.peek() == "'" || (stream.peek() == '"' && support.doubleQuote))) { @@ -94,9 +94,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { return "number"; if (stream.match(/^\.+/)) return null - // .table_name (ODBC) - // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html - if (support.ODBCdotTable && stream.match(/^[\w\d_]+/)) + if (stream.match(/^[\w\d_$#]+/)) return "variable-2"; } else if (operatorChars.test(ch)) { // operators @@ -112,19 +110,19 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { } else if (ch == '{' && (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { // dates (weird ODBC syntax) - // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-literals.html return "number"; } else { stream.eatWhile(/^[_\w\d]/); var word = stream.current().toLowerCase(); // dates (standard SQL syntax) - // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-literals.html if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/))) return "number"; if (atoms.hasOwnProperty(word)) return "atom"; - if (builtin.hasOwnProperty(word)) return "builtin"; + if (builtin.hasOwnProperty(word)) return "type"; if (keywords.hasOwnProperty(word)) return "keyword"; - if (client.hasOwnProperty(word)) return "string-2"; + if (client.hasOwnProperty(word)) return "builtin"; return null; } } @@ -207,14 +205,15 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { blockCommentStart: "/*", blockCommentEnd: "*/", lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--", - closeBrackets: "()[]{}''\"\"``" + closeBrackets: "()[]{}''\"\"``", + config: parserConfig }; }); // `identifier` function hookIdentifier(stream) { // MySQL/MariaDB identifiers - // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/identifier-qualifiers.html var ch; while ((ch = stream.next()) != null) { if (ch == "`" && !stream.eat("`")) return "variable-2"; @@ -241,11 +240,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // variables // @@prefix.varName @varName // varName can be quoted with ` or ' or " - // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/user-variables.html if (stream.eat("@")) { - stream.match(/^session\./); - stream.match(/^local\./); - stream.match(/^global\./); + stream.match('session.'); + stream.match('local.'); + stream.match('global.'); } if (stream.eat("'")) { @@ -266,12 +265,12 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // short client keyword token function hookClient(stream) { // \N means NULL - // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/null-values.html if (stream.eat("N")) { return "atom"; } // \g, etc - // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html + // ref: https://dev.mysql.com/doc/refman/8.0/en/mysql-commands.html return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null; } @@ -287,14 +286,14 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { var defaultBuiltin = "bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric" - // A generic SQL Mode. It's not a standard, it just try to support what is generally supported + // A generic SQL Mode. It's not a standard, it just tries to support what is generally supported CodeMirror.defineMIME("text/x-sql", { name: "sql", keywords: set(sqlKeywords + "begin"), builtin: set(defaultBuiltin), atoms: set("false true null unknown"), dateSQL: set("date time timestamp"), - support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") + support: set("doubleQuote binaryNumber hexNumber") }); CodeMirror.defineMIME("text/x-mssql", { @@ -321,7 +320,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^]/, dateSQL: set("date time timestamp"), - support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), + support: set("decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), hooks: { "@": hookVar, "`": hookIdentifier, @@ -332,12 +331,12 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { CodeMirror.defineMIME("text/x-mariadb", { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), - keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), + keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group group_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^]/, dateSQL: set("date time timestamp"), - support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), + support: set("decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), hooks: { "@": hookVar, "`": hookIdentifier, @@ -370,7 +369,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { "$": hookVar, // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html "\"": hookIdentifierDoublequote, - // there is also support for backtics, ref: http://sqlite.org/lang_keywords.html + // there is also support for backticks, ref: http://sqlite.org/lang_keywords.html "`": hookIdentifier } }); @@ -408,7 +407,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=]/, dateSQL: set("date timestamp"), - support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") + support: set("doubleQuote binaryNumber hexNumber") }); CodeMirror.defineMIME("text/x-pgsql", { @@ -418,12 +417,16 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // For pl/pgsql lang - https://github.com/postgres/postgres/blob/REL_11_2/src/pl/plpgsql/src/pl_scanner.c keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate alias all allocate also alter always analyse analyze and any are array array_agg array_max_cardinality as asc asensitive assert assertion assignment asymmetric at atomic attach attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli between bigint binary bit bit_length blob blocked bom boolean both breadth by c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain char char_length character character_length character_set_catalog character_set_name character_set_schema characteristics characters check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column column_name columns command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constant constraint constraint_catalog constraint_name constraint_schema constraints constructor contains content continue control conversion convert copy corr corresponding cost count covar_pop covar_samp create cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datatype date datetime_interval_code datetime_interval_precision day db deallocate debug dec decimal declare default defaults deferrable deferred defined definer degree delete delimiter delimiters dense_rank depends depth deref derived desc describe descriptor detach detail deterministic diagnostics dictionary disable discard disconnect dispatch distinct dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain double drop dump dynamic dynamic_function dynamic_function_code each element else elseif elsif empty enable encoding encrypted end end_frame end_partition endexec enforced enum equals errcode error escape event every except exception exclude excluding exclusive exec execute exists exit exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreach foreign fortran forward found frame_row free freeze from fs full function functions fusion g general generated get global go goto grant granted greatest group grouping groups handler having header hex hierarchy hint hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import in include including increment indent index indexes indicator info inherit inherits initially inline inner inout input insensitive insert instance instantiable instead int integer integrity intersect intersection interval into invoker is isnull isolation join k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like like_regex limit link listen ln load local localtime localtimestamp location locator lock locked log logged loop lower m map mapping match matched materialized max max_cardinality maxvalue member merge message message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized not nothing notice notify notnull nowait nth_value ntile null nullable nullif nulls number numeric object occurrences_regex octet_length octets of off offset oids old on only open operator option options or order ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password path percent percent_rank percentile_cont percentile_disc perform period permission pg_context pg_datatype_name pg_exception_context pg_exception_detail pg_exception_hint placing plans pli policy portion position position_regex power precedes preceding precision prepare prepared preserve primary print_strict_params prior privileges procedural procedure procedures program public publication query quote raise range rank read reads real reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result result_oid return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns reverse revoke right role rollback rollup routine routine_catalog routine_name routine_schema routines row row_count row_number rows rowtype rule savepoint scale schema schema_name schemas scope scope_catalog scope_name scope_schema scroll search second section security select selective self sensitive sequence sequences serializable server server_name session session_user set setof sets share show similar simple size skip slice smallint snapshot some source space specific specific_name specifictype sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable stacked standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t table table_name tables tablesample tablespace temp template temporary text then ties time timestamp timezone_hour timezone_minute to token top_level_count trailing transaction transaction_active transactions_committed transactions_rolled_back transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted union unique unknown unlink unlisten unlogged unnamed unnest until untyped update upper uri usage use_column use_variable user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of values var_pop var_samp varbinary varchar variable_conflict variadic varying verbose version versioning view views volatile warning when whenever where while whitespace width_bucket window with within without work wrapper write xml xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes zone"), // https://www.postgresql.org/docs/11/datatype.html - builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), + builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time zone timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), operatorChars: /^[*\/+\-%<>!=&|^\/#@?~]/, backslashStringEscapes: false, + identifierQuote: "\"", // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + hooks: { + "\"": hookIdentifierDoublequote + }, dateSQL: set("date time timestamp"), - support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast escapeConstant") + support: set("decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast escapeConstant") }); // Google's SQL-like query language, GQL @@ -445,18 +448,18 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^\/#@?~]/, dateSQL: set("date time timestamp"), - support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast") + support: set("decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast") }); // Spark SQL CodeMirror.defineMIME("text/x-sparksql", { name: "sql", - keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases datata dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"), - builtin: set("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"), + keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases data dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"), + builtin: set("abs acos acosh add_months aggregate and any approx_count_distinct approx_percentile array array_contains array_distinct array_except array_intersect array_join array_max array_min array_position array_remove array_repeat array_sort array_union arrays_overlap arrays_zip ascii asin asinh assert_true atan atan2 atanh avg base64 between bigint bin binary bit_and bit_count bit_get bit_length bit_or bit_xor bool_and bool_or boolean bround btrim cardinality case cast cbrt ceil ceiling char char_length character_length chr coalesce collect_list collect_set concat concat_ws conv corr cos cosh cot count count_if count_min_sketch covar_pop covar_samp crc32 cume_dist current_catalog current_database current_date current_timestamp current_timezone current_user date date_add date_format date_from_unix_date date_part date_sub date_trunc datediff day dayofmonth dayofweek dayofyear decimal decode degrees delimited dense_rank div double e element_at elt encode every exists exp explode explode_outer expm1 extract factorial filter find_in_set first first_value flatten float floor forall format_number format_string from_csv from_json from_unixtime from_utc_timestamp get_json_object getbit greatest grouping grouping_id hash hex hour hypot if ifnull in initcap inline inline_outer input_file_block_length input_file_block_start input_file_name inputformat instr int isnan isnotnull isnull java_method json_array_length json_object_keys json_tuple kurtosis lag last last_day last_value lcase lead least left length levenshtein like ln locate log log10 log1p log2 lower lpad ltrim make_date make_dt_interval make_interval make_timestamp make_ym_interval map map_concat map_entries map_filter map_from_arrays map_from_entries map_keys map_values map_zip_with max max_by md5 mean min min_by minute mod monotonically_increasing_id month months_between named_struct nanvl negative next_day not now nth_value ntile nullif nvl nvl2 octet_length or outputformat overlay parse_url percent_rank percentile percentile_approx pi pmod posexplode posexplode_outer position positive pow power printf quarter radians raise_error rand randn random rank rcfile reflect regexp regexp_extract regexp_extract_all regexp_like regexp_replace repeat replace reverse right rint rlike round row_number rpad rtrim schema_of_csv schema_of_json second sentences sequence sequencefile serde session_window sha sha1 sha2 shiftleft shiftright shiftrightunsigned shuffle sign signum sin sinh size skewness slice smallint some sort_array soundex space spark_partition_id split sqrt stack std stddev stddev_pop stddev_samp str_to_map string struct substr substring substring_index sum tan tanh textfile timestamp timestamp_micros timestamp_millis timestamp_seconds tinyint to_csv to_date to_json to_timestamp to_unix_timestamp to_utc_timestamp transform transform_keys transform_values translate trim trunc try_add try_divide typeof ucase unbase64 unhex uniontype unix_date unix_micros unix_millis unix_seconds unix_timestamp upper uuid var_pop var_samp variance version weekday weekofyear when width_bucket window xpath xpath_boolean xpath_double xpath_float xpath_int xpath_long xpath_number xpath_short xpath_string xxhash64 year zip_with"), atoms: set("false true null"), operatorChars: /^[*\/+\-%<>!=~&|^]/, dateSQL: set("date time timestamp"), - support: set("ODBCdotTable doubleQuote zerolessFloat") + support: set("doubleQuote zerolessFloat") }); // Esper @@ -471,6 +474,26 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { dateSQL: set("time"), support: set("decimallessFloat zerolessFloat binaryNumber hexNumber") }); + + // Trino (formerly known as Presto) + CodeMirror.defineMIME("text/x-trino", { + name: "sql", + // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4#L859-L1129 + // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/docs/src/main/sphinx/functions/list.rst + keywords: set("abs absent acos add admin after all all_match alter analyze and any any_match approx_distinct approx_most_frequent approx_percentile approx_set arbitrary array_agg array_distinct array_except array_intersect array_join array_max array_min array_position array_remove array_sort array_union arrays_overlap as asc asin at at_timezone atan atan2 authorization avg bar bernoulli beta_cdf between bing_tile bing_tile_at bing_tile_coordinates bing_tile_polygon bing_tile_quadkey bing_tile_zoom_level bing_tiles_around bit_count bitwise_and bitwise_and_agg bitwise_left_shift bitwise_not bitwise_or bitwise_or_agg bitwise_right_shift bitwise_right_shift_arithmetic bitwise_xor bool_and bool_or both by call cardinality cascade case cast catalogs cbrt ceil ceiling char2hexint checksum chr classify coalesce codepoint column columns combinations comment commit committed concat concat_ws conditional constraint contains contains_sequence convex_hull_agg copartition corr cos cosh cosine_similarity count count_if covar_pop covar_samp crc32 create cross cube cume_dist current current_catalog current_date current_groups current_path current_role current_schema current_time current_timestamp current_timezone current_user data date_add date_diff date_format date_parse date_trunc day day_of_month day_of_week day_of_year deallocate default define definer degrees delete dense_rank deny desc describe descriptor distinct distributed dow doy drop e element_at else empty empty_approx_set encoding end error escape evaluate_classifier_predictions every except excluding execute exists exp explain extract false features fetch filter final first first_value flatten floor following for format format_datetime format_number from from_base from_base32 from_base64 from_base64url from_big_endian_32 from_big_endian_64 from_encoded_polyline from_geojson_geometry from_hex from_ieee754_32 from_ieee754_64 from_iso8601_date from_iso8601_timestamp from_iso8601_timestamp_nanos from_unixtime from_unixtime_nanos from_utf8 full functions geometric_mean geometry_from_hadoop_shape geometry_invalid_reason geometry_nearest_points geometry_to_bing_tiles geometry_union geometry_union_agg grant granted grants graphviz great_circle_distance greatest group grouping groups hamming_distance hash_counts having histogram hmac_md5 hmac_sha1 hmac_sha256 hmac_sha512 hour human_readable_seconds if ignore in including index infinity initial inner input insert intersect intersection_cardinality into inverse_beta_cdf inverse_normal_cdf invoker io is is_finite is_infinite is_json_scalar is_nan isolation jaccard_index join json_array json_array_contains json_array_get json_array_length json_exists json_extract json_extract_scalar json_format json_object json_parse json_query json_size json_value keep key keys kurtosis lag last last_day_of_month last_value lateral lead leading learn_classifier learn_libsvm_classifier learn_libsvm_regressor learn_regressor least left length level levenshtein_distance like limit line_interpolate_point line_interpolate_points line_locate_point listagg ln local localtime localtimestamp log log10 log2 logical lower lpad ltrim luhn_check make_set_digest map_agg map_concat map_entries map_filter map_from_entries map_keys map_union map_values map_zip_with match match_recognize matched matches materialized max max_by md5 measures merge merge_set_digest millisecond min min_by minute mod month multimap_agg multimap_from_entries murmur3 nan natural next nfc nfd nfkc nfkd ngrams no none none_match normal_cdf normalize not now nth_value ntile null nullif nulls numeric_histogram object objectid_timestamp of offset omit on one only option or order ordinality outer output over overflow parse_data_size parse_datetime parse_duration partition partitions passing past path pattern per percent_rank permute pi position pow power preceding prepare privileges properties prune qdigest_agg quarter quotes radians rand random range rank read recursive reduce reduce_agg refresh regexp_count regexp_extract regexp_extract_all regexp_like regexp_position regexp_replace regexp_split regr_intercept regr_slope regress rename render repeat repeatable replace reset respect restrict returning reverse revoke rgb right role roles rollback rollup round row_number rows rpad rtrim running scalar schema schemas second security seek select sequence serializable session set sets sha1 sha256 sha512 show shuffle sign simplify_geometry sin skewness skip slice some soundex spatial_partitioning spatial_partitions split split_part split_to_map split_to_multimap spooky_hash_v2_32 spooky_hash_v2_64 sqrt st_area st_asbinary st_astext st_boundary st_buffer st_centroid st_contains st_convexhull st_coorddim st_crosses st_difference st_dimension st_disjoint st_distance st_endpoint st_envelope st_envelopeaspts st_equals st_exteriorring st_geometries st_geometryfromtext st_geometryn st_geometrytype st_geomfrombinary st_interiorringn st_interiorrings st_intersection st_intersects st_isclosed st_isempty st_isring st_issimple st_isvalid st_length st_linefromtext st_linestring st_multipoint st_numgeometries st_numinteriorring st_numpoints st_overlaps st_point st_pointn st_points st_polygon st_relate st_startpoint st_symdifference st_touches st_union st_within st_x st_xmax st_xmin st_y st_ymax st_ymin start starts_with stats stddev stddev_pop stddev_samp string strpos subset substr substring sum system table tables tablesample tan tanh tdigest_agg text then ties timestamp_objectid timezone_hour timezone_minute to to_base to_base32 to_base64 to_base64url to_big_endian_32 to_big_endian_64 to_char to_date to_encoded_polyline to_geojson_geometry to_geometry to_hex to_ieee754_32 to_ieee754_64 to_iso8601 to_milliseconds to_spherical_geography to_timestamp to_unixtime to_utf8 trailing transaction transform transform_keys transform_values translate trim trim_array true truncate try try_cast type typeof uescape unbounded uncommitted unconditional union unique unknown unmatched unnest update upper url_decode url_encode url_extract_fragment url_extract_host url_extract_parameter url_extract_path url_extract_port url_extract_protocol url_extract_query use user using utf16 utf32 utf8 validate value value_at_quantile values values_at_quantiles var_pop var_samp variance verbose version view week week_of_year when where width_bucket wilson_interval_lower wilson_interval_upper window with with_timezone within without word_stem work wrapper write xxhash64 year year_of_week yow zip zip_with"), + // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/core/trino-main/src/main/java/io/trino/metadata/TypeRegistry.java#L131-L168 + // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/plugin/trino-ml/src/main/java/io/trino/plugin/ml/MLPlugin.java#L35 + // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/plugin/trino-mongodb/src/main/java/io/trino/plugin/mongodb/MongoPlugin.java#L32 + // https://github.com/trinodb/trino/blob/bc7a4eeedde28684c7ae6f74cefcaf7c6e782174/plugin/trino-geospatial/src/main/java/io/trino/plugin/geospatial/GeoPlugin.java#L37 + builtin: set("array bigint bingtile boolean char codepoints color date decimal double function geometry hyperloglog int integer interval ipaddress joniregexp json json2016 jsonpath kdbtree likepattern map model objectid p4hyperloglog precision qdigest re2jregexp real regressor row setdigest smallint sphericalgeography tdigest time timestamp tinyint uuid varbinary varchar zone"), + atoms: set("false true null unknown"), + // https://trino.io/docs/current/functions/list.html#id1 + operatorChars: /^[[\]|<>=!\-+*/%]/, + dateSQL: set("date time timestamp zone"), + // hexNumber is necessary for VARBINARY literals, e.g. X'65683F' + // but it also enables 0xFF hex numbers, which Trino doesn't support. + support: set("decimallessFloat zerolessFloat hexNumber") + }); }); /* @@ -487,9 +510,12 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { Commands parsed and executed by the client (not the server). support: A list of supported syntaxes which are not common, but are supported by more than 1 DBMS. - * ODBCdotTable: .tableName * zerolessFloat: .1 - * doubleQuote + * decimallessFloat: 1. + * hexNumber: X'01AF' X'01af' x'01AF' x'01af' 0x01AF 0x01af + * binaryNumber: b'01' B'01' 0b01 + * doubleQuote: "string" + * escapeConstant: E'' * nCharCast: N'string' * charsetCast: _utf8'string' * commentHash: use # char for comments diff --git a/lib/redactor/codemirror/theme/abbott.css b/lib/redactor/codemirror/theme/abbott.css new file mode 100644 index 0000000..3e516a6 --- /dev/null +++ b/lib/redactor/codemirror/theme/abbott.css @@ -0,0 +1,268 @@ +/* + * abbott.css + * A warm, dark theme for prose and code, with pastels and pretty greens. + * + * Ported from abbott.vim (https://github.com/bcat/abbott.vim) version 2.1. + * Original design and CodeMirror port by Jonathan Rascher. + * + * This theme shares the following color palette with the Vim color scheme. + * + * Brown shades: + * bistre: #231c14 + * chocolate: #3c3022 + * cocoa: #745d42 + * vanilla_cream: #fef3b4 + * + * Red shades: + * crimson: #d80450 + * cinnabar: #f63f05 + * + * Green shades: + * dark_olive: #273900 + * forest_green: #24a507 + * chartreuse: #a0ea00 + * pastel_chartreuse: #d8ff84 + * + * Yellow shades: + * marigold: #fbb32f + * lemon_meringue: #fbec5d + * + * Blue shades: + * cornflower_blue: #3f91f1 + * periwinkle_blue: #8ccdf0 + * + * Magenta shades: + * french_pink: #ec6c99 + * lavender: #e6a2f3 + * + * Cyan shades: + * zomp: #39a78d + * seafoam_green: #00ff7f + */ + +/* Style the UI: */ + +/* Equivalent to Vim's Normal group. */ +.cm-s-abbott.CodeMirror { + background: #231c14 /* bistre */; + color: #d8ff84 /* pastel_chartreuse */; +} + +/* Roughly equivalent to Vim's LineNr group. */ +.cm-s-abbott .CodeMirror-gutters { + background: #231c14 /* bistre */; + border: none; +} +.cm-s-abbott .CodeMirror-linenumber { color: #fbec5d /* lemon_meringue */; } + +.cm-s-abbott .CodeMirror-guttermarker { color: #f63f05 /* cinnabar */; } + +/* Roughly equivalent to Vim's FoldColumn group. */ +.cm-s-abbott .CodeMirror-guttermarker-subtle { color: #fbb32f /* marigold */; } + +/* + * Roughly equivalent to Vim's CursorColumn group. (We use a brighter color + * since Vim's cursorcolumn option highlights a whole column, whereas + * CodeMirror's rule just highlights a thin line.) + */ +.cm-s-abbott .CodeMirror-ruler { border-color: #745d42 /* cocoa */; } + +/* Equivalent to Vim's Cursor group in insert mode. */ +.cm-s-abbott .CodeMirror-cursor { border-color: #a0ea00 /* chartreuse */; } + +/* Equivalent to Vim's Cursor group in normal mode. */ +.cm-s-abbott.cm-fat-cursor .CodeMirror-cursor, +.cm-s-abbott .cm-animate-fat-cursor { + /* + * CodeMirror doesn't allow changing the foreground color of the character + * under the cursor, so we can't use a reverse video effect for the cursor. + * Instead, make it semitransparent. + */ + background: rgba(160, 234, 0, 0.5) /* chartreuse */; +} +.cm-s-abbott.cm-fat-cursor .CodeMirror-cursors { + /* + * Boost the z-index so the fat cursor shows up on top of text and + * matchingbracket/matchingtag highlights. + */ + z-index: 3; +} + +/* Equivalent to Vim's Cursor group in replace mode. */ +.cm-s-abbott .CodeMirror-overwrite .CodeMirror-cursor { + border-bottom: 1px solid #a0ea00 /* chartreuse */; + border-left: none; + width: auto; +} + +/* Roughly equivalent to Vim's CursorIM group. */ +.cm-s-abbott .CodeMirror-secondarycursor { + border-color: #00ff7f /* seafoam_green */; +} + +/* Roughly equivalent to Vim's Visual group. */ +.cm-s-abbott .CodeMirror-selected, +.cm-s-abbott.CodeMirror-focused .CodeMirror-selected { + background: #273900 /* dark_olive */; +} +.cm-s-abbott .CodeMirror-line::selection, +.cm-s-abbott .CodeMirror-line > span::selection, +.cm-s-abbott .CodeMirror-line > span > span::selection { + background: #273900 /* dark_olive */; +} +.cm-s-abbott .CodeMirror-line::-moz-selection, +.cm-s-abbott .CodeMirror-line > span::-moz-selection, +.cm-s-abbott .CodeMirror-line > span > span::-moz-selection { + background: #273900 /* dark_olive */; +} + +/* Roughly equivalent to Vim's SpecialKey group. */ +.cm-s-abbott .cm-tab { color: #00ff7f /* seafoam_green */; } + +/* Equivalent to Vim's Search group. */ +.cm-s-abbott .cm-searching { + background: #fef3b4 /* vanilla_cream */ !important; + color: #231c14 /* bistre */ !important; +} + +/* Style syntax highlighting modes: */ + +/* Equivalent to Vim's Comment group. */ +.cm-s-abbott span.cm-comment { + color: #fbb32f /* marigold */; + font-style: italic; +} + +/* Equivalent to Vim's String group. */ +.cm-s-abbott span.cm-string, +.cm-s-abbott span.cm-string-2 { + color: #e6a2f3 /* lavender */; +} + +/* Equivalent to Vim's Constant group. */ +.cm-s-abbott span.cm-number, +.cm-s-abbott span.cm-string.cm-url { color: #f63f05 /* cinnabar */; } + +/* Roughly equivalent to Vim's SpecialKey group. */ +.cm-s-abbott span.cm-invalidchar { color: #00ff7f /* seafoam_green */; } + +/* Equivalent to Vim's Special group. */ +.cm-s-abbott span.cm-atom { color: #fef3b4 /* vanilla_cream */; } + +/* Equivalent to Vim's Delimiter group. */ +.cm-s-abbott span.cm-bracket, +.cm-s-abbott span.cm-punctuation { + color: #fef3b4 /* vanilla_cream */; +} + +/* Equivalent Vim's Operator group. */ +.cm-s-abbott span.cm-operator { font-weight: bold; } + +/* Roughly equivalent to Vim's Identifier group. */ +.cm-s-abbott span.cm-def, +.cm-s-abbott span.cm-variable, +.cm-s-abbott span.cm-variable-2, +.cm-s-abbott span.cm-variable-3 { + color: #8ccdf0 /* periwinkle_blue */; +} + +/* Roughly equivalent to Vim's Function group. */ +.cm-s-abbott span.cm-builtin, +.cm-s-abbott span.cm-property, +.cm-s-abbott span.cm-qualifier { + color: #3f91f1 /* cornflower_blue */; +} + +/* Equivalent to Vim's Type group. */ +.cm-s-abbott span.cm-type { color: #24a507 /* forest_green */; } + +/* Equivalent to Vim's Keyword group. */ +.cm-s-abbott span.cm-keyword { + color: #d80450 /* crimson */; + font-weight: bold; +} + +/* Equivalent to Vim's PreProc group. */ +.cm-s-abbott span.cm-meta { color: #ec6c99 /* french_pink */; } + +/* Equivalent to Vim's htmlTagName group (linked to Statement). */ +.cm-s-abbott span.cm-tag { + color: #d80450 /* crimson */; + font-weight: bold; +} + +/* Equivalent to Vim's htmlArg group (linked to Type). */ +.cm-s-abbott span.cm-attribute { color: #24a507 /* forest_green */; } + +/* Equivalent to Vim's htmlH1, markdownH1, etc. groups (linked to Title). */ +.cm-s-abbott span.cm-header { + color: #d80450 /* crimson */; + font-weight: bold; +} + +/* Equivalent to Vim's markdownRule group (linked to PreProc). */ +.cm-s-abbott span.cm-hr { color: #ec6c99 /* french_pink */; } + +/* Roughly equivalent to Vim's Underlined group. */ +.cm-s-abbott span.cm-link { color: #e6a2f3 /* lavender */; } + +/* Equivalent to Vim's diffRemoved group. */ +.cm-s-abbott span.cm-negative { + background: #d80450 /* crimson */; + color: #231c14 /* bistre */; +} + +/* Equivalent to Vim's diffAdded group. */ +.cm-s-abbott span.cm-positive { + background: #a0ea00 /* chartreuse */; + color: #231c14 /* bistre */; + font-weight: bold; +} + +/* Equivalent to Vim's Error group. */ +.cm-s-abbott span.cm-error { + background: #d80450 /* crimson */; + color: #231c14 /* bistre */; +} + +/* Style addons: */ + +/* Equivalent to Vim's MatchParen group. */ +.cm-s-abbott span.CodeMirror-matchingbracket { + background: #745d42 /* cocoa */ !important; + color: #231c14 /* bistre */ !important; + font-weight: bold; +} + +/* + * Roughly equivalent to Vim's Error group. (Vim doesn't seem to have a direct + * equivalent in its own matchparen plugin, but many syntax highlighting plugins + * mark mismatched brackets as Error.) + */ +.cm-s-abbott span.CodeMirror-nonmatchingbracket { + background: #d80450 /* crimson */ !important; + color: #231c14 /* bistre */ !important; +} + +.cm-s-abbott .CodeMirror-matchingtag, +.cm-s-abbott .cm-matchhighlight { + outline: 1px solid #39a78d /* zomp */; +} + +/* Equivalent to Vim's CursorLine group. */ +.cm-s-abbott .CodeMirror-activeline-background, +.cm-s-abbott .CodeMirror-activeline-gutter { + background: #3c3022 /* chocolate */; +} + +/* Equivalent to Vim's CursorLineNr group. */ +.cm-s-abbott .CodeMirror-activeline-gutter .CodeMirror-linenumber { + color: #d8ff84 /* pastel_chartreuse */; + font-weight: bold; +} + +/* Roughly equivalent to Vim's Folded group. */ +.cm-s-abbott .CodeMirror-foldmarker { + color: #f63f05 /* cinnabar */; + text-shadow: none; +} diff --git a/lib/redactor/codemirror/theme/ayu-dark.css b/lib/redactor/codemirror/theme/ayu-dark.css index fd41ba3..13656b9 100644 --- a/lib/redactor/codemirror/theme/ayu-dark.css +++ b/lib/redactor/codemirror/theme/ayu-dark.css @@ -9,6 +9,8 @@ .cm-s-ayu-dark .CodeMirror-guttermarker-subtle { color: #3d424d; } .cm-s-ayu-dark .CodeMirror-linenumber { color: #3d424d; } .cm-s-ayu-dark .CodeMirror-cursor { border-left: 1px solid #e6b450; } +.cm-s-ayu-dark.cm-fat-cursor .CodeMirror-cursor { background-color: #a2a8a175 !important; } +.cm-s-ayu-dark .cm-animate-fat-cursor { background-color: #a2a8a175 !important; } .cm-s-ayu-dark span.cm-comment { color: #626a73; } .cm-s-ayu-dark span.cm-atom { color: #ae81ff; } diff --git a/lib/redactor/codemirror/theme/ayu-mirage.css b/lib/redactor/codemirror/theme/ayu-mirage.css index 7a5b50c..19403ce 100644 --- a/lib/redactor/codemirror/theme/ayu-mirage.css +++ b/lib/redactor/codemirror/theme/ayu-mirage.css @@ -8,7 +8,9 @@ .cm-s-ayu-mirage .CodeMirror-guttermarker { color: white; } .cm-s-ayu-mirage .CodeMirror-guttermarker-subtle { color: rgba(112, 122, 140, 66); } .cm-s-ayu-mirage .CodeMirror-linenumber { color: rgba(61, 66, 77, 99); } -.cm-s-ayu-mirage .CodeMirror-cursor { border-left: 1px solid #ffcc66; } +.cm-s-ayu-mirage .CodeMirror-cursor { border-left: 1px solid #ffcc66; } +.cm-s-ayu-mirage.cm-fat-cursor .CodeMirror-cursor {background-color: #a2a8a175 !important;} +.cm-s-ayu-mirage .cm-animate-fat-cursor { background-color: #a2a8a175 !important; } .cm-s-ayu-mirage span.cm-comment { color: #5c6773; font-style:italic; } .cm-s-ayu-mirage span.cm-atom { color: #ae81ff; } diff --git a/lib/redactor/codemirror/theme/base16-dark.css b/lib/redactor/codemirror/theme/base16-dark.css index 026a816..b3c31ab 100644 --- a/lib/redactor/codemirror/theme/base16-dark.css +++ b/lib/redactor/codemirror/theme/base16-dark.css @@ -17,6 +17,8 @@ .cm-s-base16-dark .CodeMirror-guttermarker-subtle { color: #505050; } .cm-s-base16-dark .CodeMirror-linenumber { color: #505050; } .cm-s-base16-dark .CodeMirror-cursor { border-left: 1px solid #b0b0b0; } +.cm-s-base16-dark.cm-fat-cursor .CodeMirror-cursor { background-color: #8e8d8875 !important; } +.cm-s-base16-dark .cm-animate-fat-cursor { background-color: #8e8d8875 !important; } .cm-s-base16-dark span.cm-comment { color: #8f5536; } .cm-s-base16-dark span.cm-atom { color: #aa759f; } diff --git a/lib/redactor/codemirror/theme/bespin.css b/lib/redactor/codemirror/theme/bespin.css index 60913ba..3fd3d93 100644 --- a/lib/redactor/codemirror/theme/bespin.css +++ b/lib/redactor/codemirror/theme/bespin.css @@ -9,7 +9,7 @@ */ .cm-s-bespin.CodeMirror {background: #28211c; color: #9d9b97;} -.cm-s-bespin div.CodeMirror-selected {background: #36312e !important;} +.cm-s-bespin div.CodeMirror-selected {background: #59554f !important;} .cm-s-bespin .CodeMirror-gutters {background: #28211c; border-right: 0px;} .cm-s-bespin .CodeMirror-linenumber {color: #666666;} .cm-s-bespin .CodeMirror-cursor {border-left: 1px solid #797977 !important;} diff --git a/lib/redactor/codemirror/theme/duotone-dark.css b/lib/redactor/codemirror/theme/duotone-dark.css index 88fdc76..5373178 100644 --- a/lib/redactor/codemirror/theme/duotone-dark.css +++ b/lib/redactor/codemirror/theme/duotone-dark.css @@ -26,7 +26,7 @@ CodeMirror template by Jan T. Sott (https://github.com/idleberg), adapted by Bra .cm-s-duotone-dark span.cm-variable-2, .cm-s-duotone-dark span.cm-variable-3, .cm-s-duotone-dark span.cm-type, .cm-s-duotone-dark span.cm-string-2, .cm-s-duotone-dark span.cm-url { color: #7a63ee; } .cm-s-duotone-dark span.cm-def, .cm-s-duotone-dark span.cm-tag, .cm-s-duotone-dark span.cm-builtin, .cm-s-duotone-dark span.cm-qualifier, .cm-s-duotone-dark span.cm-header, .cm-s-duotone-dark span.cm-em { color: #eeebff; } -.cm-s-duotone-dark span.cm-bracket, .cm-s-duotone-dark span.cm-comment { color: #6c6783; } +.cm-s-duotone-dark span.cm-bracket, .cm-s-duotone-dark span.cm-comment { color: #a7a5b2; } /* using #f00 red for errors, don't think any of the colorscheme variables will stand out enough, ... maybe by giving it a background-color ... */ .cm-s-duotone-dark span.cm-error, .cm-s-duotone-dark span.cm-invalidchar { color: #f00; } diff --git a/lib/redactor/codemirror/theme/duotone-light.css b/lib/redactor/codemirror/theme/duotone-light.css index d99480f..a0a0b83 100644 --- a/lib/redactor/codemirror/theme/duotone-light.css +++ b/lib/redactor/codemirror/theme/duotone-light.css @@ -25,7 +25,7 @@ CodeMirror template by Jan T. Sott (https://github.com/idleberg), adapted by Bra .cm-s-duotone-light span.cm-variable-2, .cm-s-duotone-light span.cm-variable-3, .cm-s-duotone-light span.cm-type, .cm-s-duotone-light span.cm-string-2, .cm-s-duotone-light span.cm-url { color: #896724; } .cm-s-duotone-light span.cm-def, .cm-s-duotone-light span.cm-tag, .cm-s-duotone-light span.cm-builtin, .cm-s-duotone-light span.cm-qualifier, .cm-s-duotone-light span.cm-header, .cm-s-duotone-light span.cm-em { color: #2d2006; } -.cm-s-duotone-light span.cm-bracket, .cm-s-duotone-light span.cm-comment { color: #b6ad9a; } +.cm-s-duotone-light span.cm-bracket, .cm-s-duotone-light span.cm-comment { color: #6f6e6a; } /* using #f00 red for errors, don't think any of the colorscheme variables will stand out enough, ... maybe by giving it a background-color ... */ /* .cm-s-duotone-light span.cm-error { background: #896724; color: #728fcb; } */ diff --git a/lib/redactor/codemirror/theme/gruvbox-dark.css b/lib/redactor/codemirror/theme/gruvbox-dark.css index ded215f..d712dda 100644 --- a/lib/redactor/codemirror/theme/gruvbox-dark.css +++ b/lib/redactor/codemirror/theme/gruvbox-dark.css @@ -11,6 +11,8 @@ .cm-s-gruvbox-dark .CodeMirror-gutters {background: #282828; border-right: 0px;} .cm-s-gruvbox-dark .CodeMirror-linenumber {color: #7c6f64;} .cm-s-gruvbox-dark .CodeMirror-cursor { border-left: 1px solid #ebdbb2; } +.cm-s-gruvbox-dark.cm-fat-cursor .CodeMirror-cursor { background-color: #8e8d8875 !important; } +.cm-s-gruvbox-dark .cm-animate-fat-cursor { background-color: #8e8d8875 !important; } .cm-s-gruvbox-dark div.CodeMirror-selected { background: #928374; } .cm-s-gruvbox-dark span.cm-meta { color: #83a598; } diff --git a/lib/redactor/codemirror/theme/juejin.css b/lib/redactor/codemirror/theme/juejin.css new file mode 100644 index 0000000..38cf7fe --- /dev/null +++ b/lib/redactor/codemirror/theme/juejin.css @@ -0,0 +1,30 @@ +.cm-s-juejin.CodeMirror { + background: #f8f9fa; +} +.cm-s-juejin .cm-header, +.cm-s-juejin .cm-def { + color: #1ba2f0; +} +.cm-s-juejin .cm-comment { + color: #009e9d; +} +.cm-s-juejin .cm-quote, +.cm-s-juejin .cm-link, +.cm-s-juejin .cm-strong, +.cm-s-juejin .cm-attribute { + color: #fd7741; +} +.cm-s-juejin .cm-url, +.cm-s-juejin .cm-keyword, +.cm-s-juejin .cm-builtin { + color: #bb51b8; +} +.cm-s-juejin .cm-hr { + color: #909090; +} +.cm-s-juejin .cm-tag { + color: #107000; +} +.cm-s-juejin .cm-variable-2 { + color: #0050a0; +} diff --git a/lib/redactor/codemirror/theme/material-ocean.css b/lib/redactor/codemirror/theme/material-ocean.css index 86a6f3c..404178d 100644 --- a/lib/redactor/codemirror/theme/material-ocean.css +++ b/lib/redactor/codemirror/theme/material-ocean.css @@ -24,6 +24,12 @@ .cm-s-material-ocean .CodeMirror-cursor { border-left: 1px solid #FFCC00; } +.cm-s-material-ocean.cm-fat-cursor .CodeMirror-cursor { + background-color: #a2a8a175 !important; +} +.cm-s-material-ocean .cm-animate-fat-cursor { + background-color: #a2a8a175 !important; +} .cm-s-material-ocean div.CodeMirror-selected { background: rgba(113, 124, 180, 0.2); @@ -132,4 +138,4 @@ .cm-s-material-ocean .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; -} \ No newline at end of file +} diff --git a/lib/redactor/codemirror/theme/material-palenight.css b/lib/redactor/codemirror/theme/material-palenight.css index 66d53dd..6712c43 100644 --- a/lib/redactor/codemirror/theme/material-palenight.css +++ b/lib/redactor/codemirror/theme/material-palenight.css @@ -24,6 +24,12 @@ .cm-s-material-palenight .CodeMirror-cursor { border-left: 1px solid #FFCC00; } +.cm-s-material-palenight.cm-fat-cursor .CodeMirror-cursor { + background-color: #607c8b80 !important; +} +.cm-s-material-palenight .cm-animate-fat-cursor { + background-color: #607c8b80 !important; +} .cm-s-material-palenight div.CodeMirror-selected { background: rgba(113, 124, 180, 0.2); @@ -132,4 +138,4 @@ .cm-s-material-palenight .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; -} \ No newline at end of file +} diff --git a/lib/redactor/codemirror/theme/material.css b/lib/redactor/codemirror/theme/material.css index 9ac17a3..a784849 100644 --- a/lib/redactor/codemirror/theme/material.css +++ b/lib/redactor/codemirror/theme/material.css @@ -24,6 +24,12 @@ .cm-s-material .CodeMirror-cursor { border-left: 1px solid #FFCC00; } +.cm-s-material.cm-fat-cursor .CodeMirror-cursor { + background-color: #5d6d5c80 !important; +} +.cm-s-material .cm-animate-fat-cursor { + background-color: #5d6d5c80 !important; +} .cm-s-material div.CodeMirror-selected { background: rgba(128, 203, 196, 0.2); @@ -132,4 +138,4 @@ .cm-s-material .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; -} \ No newline at end of file +} diff --git a/lib/redactor/codemirror/theme/oceanic-next.css b/lib/redactor/codemirror/theme/oceanic-next.css index 296277b..f3d0d08 100644 --- a/lib/redactor/codemirror/theme/oceanic-next.css +++ b/lib/redactor/codemirror/theme/oceanic-next.css @@ -16,6 +16,8 @@ .cm-s-oceanic-next .CodeMirror-guttermarker-subtle { color: #d0d0d0; } .cm-s-oceanic-next .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-oceanic-next .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } +.cm-s-oceanic-next.cm-fat-cursor .CodeMirror-cursor { background-color: #a2a8a175 !important; } +.cm-s-oceanic-next .cm-animate-fat-cursor { background-color: #a2a8a175 !important; } .cm-s-oceanic-next span.cm-comment { color: #65737E; } .cm-s-oceanic-next span.cm-atom { color: #C594C5; } diff --git a/lib/redactor/codemirror/theme/solarized.css b/lib/redactor/codemirror/theme/solarized.css index fcd1d70..e978fec 100644 --- a/lib/redactor/codemirror/theme/solarized.css +++ b/lib/redactor/codemirror/theme/solarized.css @@ -35,12 +35,10 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png .cm-s-solarized.cm-s-dark { color: #839496; background-color: #002b36; - text-shadow: #002b36 0 1px; } .cm-s-solarized.cm-s-light { background-color: #fdf6e3; color: #657b83; - text-shadow: #eee8d5 0 1px; } .cm-s-solarized .CodeMirror-widget { @@ -99,7 +97,7 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png .cm-s-solarized.cm-s-light div.CodeMirror-selected { background: #eee8d5; } .cm-s-solarized.cm-s-light .CodeMirror-line::selection, .cm-s-light .CodeMirror-line > span::selection, .cm-s-light .CodeMirror-line > span > span::selection { background: #eee8d5; } -.cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection, .cm-s-ligh .CodeMirror-line > span::-moz-selection, .cm-s-ligh .CodeMirror-line > span > span::-moz-selection { background: #eee8d5; } +.cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection, .cm-s-light .CodeMirror-line > span::-moz-selection, .cm-s-light .CodeMirror-line > span > span::-moz-selection { background: #eee8d5; } /* Editor styling */ @@ -126,7 +124,6 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png .cm-s-solarized.cm-s-dark .CodeMirror-linenumber { color: #586e75; - text-shadow: #021014 0 -1px; } /* Light */ diff --git a/lib/redactor/codemirror/theme/zenburn.css b/lib/redactor/codemirror/theme/zenburn.css index 781c40a..4eb4247 100644 --- a/lib/redactor/codemirror/theme/zenburn.css +++ b/lib/redactor/codemirror/theme/zenburn.css @@ -11,7 +11,7 @@ .cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; } .cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; } .cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white; } -.cm-s-zenburn { background-color: #3f3f3f; color: #dcdccc; } +.cm-s-zenburn.CodeMirror { background-color: #3f3f3f; color: #dcdccc; } .cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; } .cm-s-zenburn span.cm-comment { color: #7f9f7f; } .cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; }