diff --git a/applications/admin/static/codemirror/addon/comment/comment.js b/applications/admin/static/codemirror/addon/comment/comment.js index cd2123e1..5975b0bf 100644 --- a/applications/admin/static/codemirror/addon/comment/comment.js +++ b/applications/admin/static/codemirror/addon/comment/comment.js @@ -96,8 +96,9 @@ for (var i = start; i <= end; ++i) { var line = self.getLine(i); var found = line.indexOf(lineString); + if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; - if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment; + if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; lines.push(line); } self.operation(function() { @@ -124,7 +125,10 @@ endLine = self.getLine(--end); close = endLine.lastIndexOf(endString); } - if (open == -1 || close == -1) return false; + if (open == -1 || close == -1 || + !/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) || + !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1)))) + return false; self.operation(function() { self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), diff --git a/applications/admin/static/codemirror/addon/dialog/dialog.js b/applications/admin/static/codemirror/addon/dialog/dialog.js index 71e22874..41d7bf86 100644 --- a/applications/admin/static/codemirror/addon/dialog/dialog.js +++ b/applications/admin/static/codemirror/addon/dialog/dialog.js @@ -10,11 +10,22 @@ } else { dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; } - dialog.innerHTML = template; + if (typeof template == "string") { + dialog.innerHTML = template; + } else { // Assuming it's a detached DOM element. + dialog.appendChild(template); + } return dialog; } + function closeNotification(cm, newVal) { + if (cm.state.currentNotificationClose) + cm.state.currentNotificationClose(); + cm.state.currentNotificationClose = newVal; + } + CodeMirror.defineExtension("openDialog", function(template, callback, options) { + closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var closed = false, me = this; function close() { @@ -24,6 +35,7 @@ } var inp = dialog.getElementsByTagName("input")[0], button; if (inp) { + if (options && options.value) inp.value = options.value; CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { @@ -51,6 +63,7 @@ }); CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { + closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var buttons = dialog.getElementsByTagName("button"); var closed = false, me = this, blurring = 1; @@ -77,4 +90,33 @@ CodeMirror.on(b, "focus", function() { ++blurring; }); } }); + + /* + * openNotification + * Opens a notification, that can be closed with an optional timer + * (default 5000ms timer) and always closes on click. + * + * If a notification is opened while another is opened, it will close the + * currently opened one and open the new one immediately. + */ + CodeMirror.defineExtension("openNotification", function(template, options) { + closeNotification(this, close); + var dialog = dialogDiv(this, template, options && options.bottom); + var duration = options && (options.duration === undefined ? 5000 : options.duration); + var closed = false, doneTimer; + + function close() { + if (closed) return; + closed = true; + clearTimeout(doneTimer); + dialog.parentNode.removeChild(dialog); + } + + CodeMirror.on(dialog, 'click', function(e) { + CodeMirror.e_preventDefault(e); + close(); + }); + if (duration) + doneTimer = setTimeout(close, options.duration); + }); })(); diff --git a/applications/admin/static/codemirror/addon/display/fullscreen.js b/applications/admin/static/codemirror/addon/display/fullscreen.js index 3c31e97a..a442f6a4 100644 --- a/applications/admin/static/codemirror/addon/display/fullscreen.js +++ b/applications/admin/static/codemirror/addon/display/fullscreen.js @@ -12,7 +12,8 @@ var wrap = cm.getWrapperElement(); cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, width: wrap.style.width, height: wrap.style.height}; - wrap.style.width = wrap.style.height = ""; + wrap.style.width = ""; + wrap.style.height = "auto"; wrap.className += " CodeMirror-fullscreen"; document.documentElement.style.overflow = "hidden"; cm.refresh(); diff --git a/applications/admin/static/codemirror/addon/display/placeholder.js b/applications/admin/static/codemirror/addon/display/placeholder.js index 18f9dff3..748afe72 100644 --- a/applications/admin/static/codemirror/addon/display/placeholder.js +++ b/applications/admin/static/codemirror/addon/display/placeholder.js @@ -2,12 +2,10 @@ CodeMirror.defineOption("placeholder", "", function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - cm.on("focus", onFocus); cm.on("blur", onBlur); cm.on("change", onChange); onChange(cm); } else if (!val && prev) { - cm.off("focus", onFocus); cm.off("blur", onBlur); cm.off("change", onChange); clearPlaceholder(cm); @@ -33,9 +31,6 @@ cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); } - function onFocus(cm) { - clearPlaceholder(cm); - } function onBlur(cm) { if (isEmpty(cm)) setPlaceholder(cm); } @@ -43,7 +38,6 @@ var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); - if (cm.hasFocus()) return; if (empty) setPlaceholder(cm); else clearPlaceholder(cm); } diff --git a/applications/admin/static/codemirror/addon/edit/closebrackets.js b/applications/admin/static/codemirror/addon/edit/closebrackets.js index 88718b77..0575222b 100644 --- a/applications/admin/static/codemirror/addon/edit/closebrackets.js +++ b/applications/admin/static/codemirror/addon/edit/closebrackets.js @@ -28,7 +28,7 @@ var map = { name : "autoCloseBrackets", Backspace: function(cm) { - if (cm.somethingSelected()) return CodeMirror.Pass; + if (cm.somethingSelected() || cm.getOption("disableInput")) return CodeMirror.Pass; var cur = cm.getCursor(), around = charsAround(cm, cur); if (around && pairs.indexOf(around) % 2 == 0) cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); @@ -49,7 +49,8 @@ else cm.execCommand("goCharRight"); } map["'" + left + "'"] = function(cm) { - if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment") + if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment" || + cm.getOption("disableInput")) return CodeMirror.Pass; if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; @@ -70,7 +71,8 @@ function buildExplodeHandler(pairs) { return function(cm) { var cur = cm.getCursor(), around = charsAround(cm, cur); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + if (!around || pairs.indexOf(around) % 2 != 0 || cm.getOption("disableInput")) + return CodeMirror.Pass; cm.operation(function() { var newPos = CodeMirror.Pos(cur.line + 1, 0); cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input"); diff --git a/applications/admin/static/codemirror/addon/edit/closetag.js b/applications/admin/static/codemirror/addon/edit/closetag.js index d6a8fafd..cad776a7 100644 --- a/applications/admin/static/codemirror/addon/edit/closetag.js +++ b/applications/admin/static/codemirror/addon/edit/closetag.js @@ -24,16 +24,15 @@ (function() { CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { - if (val && (old == CodeMirror.Init || !old)) { - var map = {name: "autoCloseTags"}; - if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { return autoCloseSlash(cm); }; - if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { return autoCloseGT(cm); }; - cm.addKeyMap(map); - } else if (!val && (old != CodeMirror.Init && old)) { + if (old != CodeMirror.Init && old) cm.removeKeyMap("autoCloseTags"); - } + if (!val) return; + var map = {name: "autoCloseTags"}; + if (typeof val != "object" || val.whenClosing) + map["'/'"] = function(cm) { return autoCloseSlash(cm); }; + if (typeof val != "object" || val.whenOpening) + map["'>'"] = function(cm) { return autoCloseGT(cm); }; + cm.addKeyMap(map); }); var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", @@ -44,7 +43,7 @@ function autoCloseGT(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; + if (inner.mode.name != "xml" || !state.tagName || cm.getOption("disableInput")) return CodeMirror.Pass; var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); @@ -54,9 +53,13 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "tag" && state.type == "closeTag" || + if (!tagName || + tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || + tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") == (tok.string.length - 1) || // match something like - dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) + dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || + CodeMirror.scanForClosingTag && CodeMirror.scanForClosingTag(cm, pos, tagName, + Math.min(cm.lastLine() + 1, pos.line + 50))) return CodeMirror.Pass; var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; @@ -64,15 +67,18 @@ cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", {head: curPos, anchor: curPos}); if (doIndent) { - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); + cm.indentLine(pos.line + 1, null, true); + cm.indentLine(pos.line + 2, null); } } function autoCloseSlash(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (tok.string.charAt(0) != "<" || tok.start != pos.ch - 1 || inner.mode.name != "xml") return CodeMirror.Pass; + if (tok.type == "string" || tok.string.charAt(0) != "<" || + tok.start != pos.ch - 1 || inner.mode.name != "xml" || + cm.getOption("disableInput")) + return CodeMirror.Pass; var tagName = state.context && state.context.tagName; if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); diff --git a/applications/admin/static/codemirror/addon/edit/continuelist.js b/applications/admin/static/codemirror/addon/edit/continuelist.js index 826d17d7..190b41dc 100644 --- a/applications/admin/static/codemirror/addon/edit/continuelist.js +++ b/applications/admin/static/codemirror/addon/edit/continuelist.js @@ -5,6 +5,8 @@ unorderedBullets = '*+-'; CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var pos = cm.getCursor(), inList = cm.getStateAfter(pos.line).list !== false, match; diff --git a/applications/admin/static/codemirror/addon/edit/matchbrackets.js b/applications/admin/static/codemirror/addon/edit/matchbrackets.js index 131fe831..465b6cca 100644 --- a/applications/admin/static/codemirror/addon/edit/matchbrackets.js +++ b/applications/admin/static/codemirror/addon/edit/matchbrackets.js @@ -8,6 +8,7 @@ function findMatchingBracket(cm, where, strict) { var state = cm.state.matchBrackets; var maxScanLen = (state && state.maxScanLineLength) || 10000; + var maxScanLines = (state && state.maxScanLines) || 100; var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; @@ -32,7 +33,7 @@ } } } - for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { + for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) { if (i == cur.line) found = scan(line, i, pos); else found = scan(cm.getLineHandle(i), i); if (found) break; @@ -53,7 +54,7 @@ var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); // 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.display.input.focus(); var clear = function() { cm.operation(function() { one.clear(); two && two.clear(); }); diff --git a/applications/admin/static/codemirror/addon/fold/comment-fold.js b/applications/admin/static/codemirror/addon/fold/comment-fold.js index a064cf8f..26e72f09 100644 --- a/applications/admin/static/codemirror/addon/fold/comment-fold.js +++ b/applications/admin/static/codemirror/addon/fold/comment-fold.js @@ -1,4 +1,6 @@ -CodeMirror.registerHelper("fold", "comment", function(cm, start) { +CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { + return mode.blockCommentStart && mode.blockCommentEnd; +}, function(cm, start) { var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; if (!startToken || !endToken) return; var line = start.line, lineText = cm.getLine(line); diff --git a/applications/admin/static/codemirror/addon/fold/foldcode.js b/applications/admin/static/codemirror/addon/fold/foldcode.js index c497bc29..5c00d709 100644 --- a/applications/admin/static/codemirror/addon/fold/foldcode.js +++ b/applications/admin/static/codemirror/addon/fold/foldcode.js @@ -3,8 +3,7 @@ function doFold(cm, pos, options, force) { var finder = options && (options.call ? options : options.rangeFinder); - if (!finder) finder = cm.getHelper(pos, "fold"); - if (!finder) return; + if (!finder) finder = CodeMirror.fold.auto; if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); var minSize = options && options.minFoldSize || 0; @@ -63,6 +62,10 @@ doFold(this, pos, options, force); }); + CodeMirror.commands.fold = function(cm) { + cm.foldCode(cm.getCursor()); + }; + CodeMirror.registerHelper("fold", "combine", function() { var funcs = Array.prototype.slice.call(arguments, 0); return function(cm, start) { @@ -72,4 +75,12 @@ } }; }); + + CodeMirror.registerHelper("fold", "auto", function(cm, start) { + var helpers = cm.getHelpers(start, "fold"); + for (var i = 0; i < helpers.length; i++) { + var cur = helpers[i](cm, start); + if (cur) return cur; + } + }); })(); diff --git a/applications/admin/static/codemirror/addon/fold/foldgutter.js b/applications/admin/static/codemirror/addon/fold/foldgutter.js index e3c52bc2..a4f3bb31 100644 --- a/applications/admin/static/codemirror/addon/fold/foldgutter.js +++ b/applications/admin/static/codemirror/addon/fold/foldgutter.js @@ -62,7 +62,7 @@ if (isFolded(cm, cur)) { mark = marker(opts.indicatorFolded); } else { - var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold"); + var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto; var range = func && func(cm, pos); if (range && range.from.line + 1 < range.to.line) mark = marker(opts.indicatorOpen); @@ -88,14 +88,14 @@ } function onChange(cm) { - var state = cm.state.foldGutter; + var state = cm.state.foldGutter, opts = cm.state.foldGutter.options; state.from = state.to = 0; clearTimeout(state.changeUpdate); - state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, 600); + state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600); } function onViewportChange(cm) { - var state = cm.state.foldGutter; + var state = cm.state.foldGutter, opts = cm.state.foldGutter.options; clearTimeout(state.changeUpdate); state.changeUpdate = setTimeout(function() { var vp = cm.getViewport(); @@ -113,7 +113,7 @@ } }); } - }, 400); + }, opts.updateViewportTimeSpan || 400); } function onFold(cm, from) { diff --git a/applications/admin/static/codemirror/addon/fold/indent-fold.js b/applications/admin/static/codemirror/addon/fold/indent-fold.js index 7ae005d9..434c2bc5 100644 --- a/applications/admin/static/codemirror/addon/fold/indent-fold.js +++ b/applications/admin/static/codemirror/addon/fold/indent-fold.js @@ -1,27 +1,30 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { - var lastLine = cm.lastLine(), - tabSize = cm.getOption("tabSize"), - firstLine = cm.getLine(start.line); - if (!tabSize || !firstLine) return; - var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); - - function foldEnded(curColumn, prevColumn) { - return curColumn < myIndent || - (curColumn == myIndent && prevColumn >= myIndent) || - (curColumn > myIndent && i == lastLine); - } - - for (var i = start.line + 1; i <= lastLine; i++) { - var curColumn = CodeMirror.countColumn(cm.getLine(i), null, tabSize); - var prevColumn = CodeMirror.countColumn(cm.getLine(i-1), null, tabSize); - - if (foldEnded(curColumn, prevColumn)) { - var lastFoldLineNumber = curColumn > myIndent && i == lastLine ? i : i-1; - var lastFoldLine = cm.getLine(lastFoldLineNumber); - return {from: CodeMirror.Pos(start.line, firstLine.length), - to: CodeMirror.Pos(lastFoldLineNumber, lastFoldLine.length)}; + var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); + if (!/\S/.test(firstLine)) return; + var getIndent = function(line) { + return CodeMirror.countColumn(line, null, tabSize); + }; + var myIndent = getIndent(firstLine); + var lastLineInFold = null; + // Go through lines until we find a line that definitely doesn't belong in + // the block we're folding, or to the end. + for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { + var curLine = cm.getLine(i); + var curIndent = getIndent(curLine); + if (curIndent > myIndent) { + // Lines with a greater indent are considered part of the block. + lastLineInFold = i; + } else if (!/\S/.test(curLine)) { + // Empty lines might be breaks within the block we're trying to fold. + } else { + // A non-empty line at an indent equal to or less than ours marks the + // start of another block. + break; } } + if (lastLineInFold) return { + from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) + }; }); - CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated diff --git a/applications/admin/static/codemirror/addon/fold/xml-fold.js b/applications/admin/static/codemirror/addon/fold/xml-fold.js index 88a107c4..db5aed70 100644 --- a/applications/admin/static/codemirror/addon/fold/xml-fold.js +++ b/applications/admin/static/codemirror/addon/fold/xml-fold.js @@ -164,4 +164,10 @@ if (close) return {open: open, close: close}; } }; + + // Used by addon/edit/closetag.js + CodeMirror.scanForClosingTag = function(cm, pos, name, end) { + var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); + return !!findMatchingClose(iter, name); + }; })(); diff --git a/applications/admin/static/codemirror/addon/hint/javascript-hint.js b/applications/admin/static/codemirror/addon/hint/javascript-hint.js index 513fb782..c347c206 100644 --- a/applications/admin/static/codemirror/addon/hint/javascript-hint.js +++ b/applications/admin/static/codemirror/addon/hint/javascript-hint.js @@ -21,6 +21,7 @@ function scriptHint(editor, keywords, getToken, options) { // Find the token at the cursor var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; + if (/\b(?:string|comment)\b/.test(token.type)) return; token.state = CodeMirror.innerMode(editor.getMode(), token.state).state; // If it's not a 'word-style' token, ignore the token. @@ -86,7 +87,7 @@ function getCompletions(token, context, keywords, options) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { if (typeof obj == "string") forEach(stringProps, maybeAdd); diff --git a/applications/admin/static/codemirror/addon/hint/pig-hint.js b/applications/admin/static/codemirror/addon/hint/pig-hint.js index 7ef336ce..6c0c5237 100644 --- a/applications/admin/static/codemirror/addon/hint/pig-hint.js +++ b/applications/admin/static/codemirror/addon/hint/pig-hint.js @@ -84,7 +84,7 @@ function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { diff --git a/applications/admin/static/codemirror/addon/hint/python-hint.js b/applications/admin/static/codemirror/addon/hint/python-hint.js index 98d2a589..248284e8 100644 --- a/applications/admin/static/codemirror/addon/hint/python-hint.js +++ b/applications/admin/static/codemirror/addon/hint/python-hint.js @@ -31,10 +31,6 @@ var completionList = getCompletions(token, context); completionList = completionList.sort(); - //prevent autocomplete for last word, instead show dropdown with one word - if(completionList.length == 1) { - completionList.push(" "); - } return {list: completionList, from: CodeMirror.Pos(cur.line, token.start), @@ -66,7 +62,7 @@ function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(_obj) { diff --git a/applications/admin/static/codemirror/addon/hint/show-hint.js b/applications/admin/static/codemirror/addon/hint/show-hint.js index dbf41552..7e03d113 100644 --- a/applications/admin/static/codemirror/addon/hint/show-hint.js +++ b/applications/admin/static/codemirror/addon/hint/show-hint.js @@ -1,11 +1,16 @@ (function() { "use strict"; + var HINT_ELEMENT_CLASS = "CodeMirror-hint"; + var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; + CodeMirror.showHint = function(cm, getHints, options) { // We want a single cursor position. if (cm.somethingSelected()) return; - if (getHints == null) getHints = cm.getHelper(cm.getCursor(), "hint"); - if (getHints == null) return; + if (getHints == null) { + if (options && options.async) return; + else getHints = CodeMirror.hint.auto; + } if (cm.state.completionActive) cm.state.completionActive.close(); @@ -42,6 +47,7 @@ var completion = data.list[i]; if (completion.hint) completion.hint(this.cm, data, completion); else this.cm.replaceRange(getText(completion), data.from, data.to); + CodeMirror.signal(data, "pick", completion); this.close(); }, @@ -58,10 +64,15 @@ this.widget = new Widget(this, data); CodeMirror.signal(data, "shown"); - var debounce = null, completion = this, finished; + var debounce = 0, completion = this, finished; var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/; var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length; + var requestAnimationFrame = window.requestAnimationFrame || function(fn) { + return setTimeout(fn, 1000/60); + }; + var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; + function done() { if (finished) return; finished = true; @@ -85,15 +96,22 @@ completion.widget = new Widget(completion, data); } + function clearDebounce() { + if (debounce) { + cancelAnimationFrame(debounce); + debounce = 0; + } + } + function activity() { - clearTimeout(debounce); + clearDebounce(); var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line); if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch || pos.ch < startPos.ch || completion.cm.somethingSelected() || (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) { completion.close(); } else { - debounce = setTimeout(update, 170); + debounce = requestAnimationFrame(update); if (completion.widget) completion.widget.close(); } } @@ -140,6 +158,13 @@ return ourMap; } + function getHintElement(hintsElement, el) { + while (el && el != hintsElement) { + if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; + el = el.parentNode; + } + } + function Widget(completion, data) { this.completion = completion; this.data = data; @@ -147,12 +172,12 @@ var hints = this.hints = document.createElement("ul"); hints.className = "CodeMirror-hints"; - this.selectedHint = 0; + this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0; var completions = data.list; for (var i = 0; i < completions.length; ++i) { var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; - var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active"); + var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); if (cur.className != null) className = cur.className + " " + className; elt.className = className; if (cur.render) cur.render(elt, data, cur); @@ -216,13 +241,18 @@ }); CodeMirror.on(hints, "dblclick", function(e) { - var t = e.target || e.srcElement; - if (t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} + var t = getHintElement(hints, e.target || e.srcElement); + if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} }); + CodeMirror.on(hints, "click", function(e) { - var t = e.target || e.srcElement; - if (t.hintId != null) widget.changeActive(t.hintId); + var t = getHintElement(hints, e.target || e.srcElement); + if (t && t.hintId != null) { + widget.changeActive(t.hintId); + if (options.completeOnSingleClick) widget.pick(); + } }); + CodeMirror.on(hints, "mousedown", function() { setTimeout(function(){cm.focus();}, 20); }); @@ -257,9 +287,9 @@ i = avoidWrap ? 0 : this.data.list.length - 1; if (this.selectedHint == i) return; var node = this.hints.childNodes[this.selectedHint]; - node.className = node.className.replace(" CodeMirror-hint-active", ""); + node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); node = this.hints.childNodes[this.selectedHint = i]; - node.className += " CodeMirror-hint-active"; + node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; if (node.offsetTop < this.hints.scrollTop) this.hints.scrollTop = node.offsetTop - 3; else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) @@ -271,4 +301,35 @@ return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; } }; + + CodeMirror.registerHelper("hint", "auto", function(cm, options) { + var helpers = cm.getHelpers(cm.getCursor(), "hint"); + if (helpers.length) { + for (var i = 0; i < helpers.length; i++) { + var cur = helpers[i](cm, options); + if (cur && cur.list.length) return cur; + } + } else { + var words = cm.getHelper(cm.getCursor(), "hintWords"); + if (words) return CodeMirror.hint.fromList(cm, {words: words}); + } + }); + + CodeMirror.registerHelper("hint", "fromList", function(cm, options) { + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var found = []; + for (var i = 0; i < options.words.length; i++) { + var word = options.words[i]; + if (word.slice(0, token.string.length) == token.string) + found.push(word); + } + + if (found.length) return { + list: found, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end) + }; + }); + + CodeMirror.commands.autocomplete = CodeMirror.showHint; })(); diff --git a/applications/admin/static/codemirror/addon/hint/xml-hint.js b/applications/admin/static/codemirror/addon/hint/xml-hint.js index a7217434..69f2b771 100644 --- a/applications/admin/static/codemirror/addon/hint/xml-hint.js +++ b/applications/admin/static/codemirror/addon/hint/xml-hint.js @@ -20,13 +20,13 @@ var cx = inner.state.context, curTag = cx && tags[cx.tagName]; var childList = cx ? curTag && curTag.children : tags["!top"]; if (childList) { - for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0) + for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) result.push("<" + childList[i]); } else { - for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0)) + for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) result.push("<" + name); } - if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0)) + if (cx && (!prefix || ("/" + cx.tagName).lastIndexOf(prefix, 0) == 0)) result.push(""); } else { // Attribute completion @@ -46,14 +46,14 @@ } replaceToken = true; } - for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0) + for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) result.push(quote + atValues[i] + quote); } else { // An attribute name if (token.type == "attribute") { prefix = token.string; replaceToken = true; } - for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0)) + for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) result.push(attr); } } diff --git a/applications/admin/static/codemirror/addon/lint/lint.js b/applications/admin/static/codemirror/addon/lint/lint.js index b502ee41..6121735d 100644 --- a/applications/admin/static/codemirror/addon/lint/lint.js +++ b/applications/admin/static/codemirror/addon/lint/lint.js @@ -112,7 +112,7 @@ if (options.async) options.getAnnotations(cm, updateLinting, options); else - updateLinting(cm, options.getAnnotations(cm.getValue(), options)); + updateLinting(cm, options.getAnnotations(cm.getValue(), options.options)); } function updateLinting(cm, annotationsNotSorted) { diff --git a/applications/admin/static/codemirror/addon/merge/merge.js b/applications/admin/static/codemirror/addon/merge/merge.js index 70341e4d..89f85265 100644 --- a/applications/admin/static/codemirror/addon/merge/merge.js +++ b/applications/admin/static/codemirror/addon/merge/merge.js @@ -29,7 +29,7 @@ this.edit = this.mv.edit; this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options))); - this.diff = getDiff(orig, options.value); + this.diff = getDiff(asString(orig), asString(options.value)); this.diffOutOfDate = false; this.showDifferences = options.showDifferences !== false; @@ -352,6 +352,11 @@ } }; + function asString(obj) { + if (typeof obj == "string") return obj; + else return obj.getValue(); + } + // Operations on diffs var dmp = new diff_match_patch(); diff --git a/applications/admin/static/codemirror/addon/mode/multiplex.js b/applications/admin/static/codemirror/addon/mode/multiplex.js index 32cc579c..614ab1ad 100644 --- a/applications/admin/static/codemirror/addon/mode/multiplex.js +++ b/applications/admin/static/codemirror/addon/mode/multiplex.js @@ -47,7 +47,11 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return outerToken; } else { var curInner = state.innerActive, oldContent = stream.string; - var found = indexOf(oldContent, curInner.close, stream.pos); + if (!curInner.close && stream.sol()) { + state.innerActive = state.inner = null; + return this.token(stream, state); + } + var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos) : -1; if (found == stream.pos) { stream.match(curInner.close); state.innerActive = state.inner = null; @@ -56,8 +60,6 @@ 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; - var cur = stream.current(), found = cur.indexOf(curInner.close); - if (found > -1) stream.backUp(cur.length - found); if (curInner.innerStyle) { if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle; diff --git a/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js b/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js index d117166c..ec06ba11 100644 --- a/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js +++ b/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js @@ -10,6 +10,7 @@ function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { this.pos = this.start = 0; this.string = string; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, @@ -41,7 +42,7 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return this.start;}, + column: function() {return this.start - this.lineStart;}, indentation: function() {return 0;}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { @@ -58,7 +59,12 @@ StringStream.prototype = { return match; } }, - current: function(){return this.string.slice(this.start, this.pos);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; CodeMirror.StringStream = StringStream; @@ -69,17 +75,26 @@ CodeMirror.startState = function (mode, a1, a2) { var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; CodeMirror.defineMode = function (name, mode) { modes[name] = mode; }; CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; }; -CodeMirror.getMode = function (options, spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) +CodeMirror.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - if (typeof spec == "string") - var mname = spec, config = {}; - else if (spec != null) - var mname = spec.name, config = spec; - var mfactory = modes[mname]; - if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, config || {}); + } 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); @@ -122,7 +137,7 @@ CodeMirror.runMode = function (string, modespec, callback, options) { }; } - var lines = splitLines(string), state = CodeMirror.startState(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]); diff --git a/applications/admin/static/codemirror/addon/runmode/runmode.js b/applications/admin/static/codemirror/addon/runmode/runmode.js index 7aafa2ad..2cafa811 100644 --- a/applications/admin/static/codemirror/addon/runmode/runmode.js +++ b/applications/admin/static/codemirror/addon/runmode/runmode.js @@ -43,7 +43,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) { }; } - var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); + 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]); diff --git a/applications/admin/static/codemirror/addon/runmode/runmode.node.js b/applications/admin/static/codemirror/addon/runmode/runmode.node.js index 0f1088fa..e8bccb0c 100644 --- a/applications/admin/static/codemirror/addon/runmode/runmode.node.js +++ b/applications/admin/static/codemirror/addon/runmode/runmode.node.js @@ -5,6 +5,7 @@ function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { this.pos = this.start = 0; this.string = string; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, @@ -36,7 +37,7 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return this.start;}, + column: function() {return this.start - this.lineStart;}, indentation: function() {return 0;}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { @@ -53,7 +54,12 @@ StringStream.prototype = { return match; } }, - current: function(){return this.string.slice(this.start, this.pos);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; exports.StringStream = StringStream; @@ -76,21 +82,26 @@ exports.defineMode("null", function() { }); exports.defineMIME("text/plain", "null"); -exports.getMode = function(options, spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) +exports.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - if (typeof spec == "string") - var mname = spec, config = {}; - else if (spec != null) - var mname = spec.name, config = spec; - var mfactory = modes[mname]; - if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, config || {}); + } 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"}; }; +exports.getMode = function(options, spec) { + spec = exports.resolveMode(mimeModes[spec]); + var mfactory = modes[spec.name]; + if (!mfactory) throw new Error("Unknown mode: " + spec); + return mfactory(options, spec); +}; +exports.registerHelper = exports.registerGlobalHelper = Math.min; exports.runMode = function(string, modespec, callback) { var mode = exports.getMode({indentUnit: 2}, modespec); - var lines = splitLines(string), state = exports.startState(mode); + var lines = splitLines(string), state = (options && options.state) || exports.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new exports.StringStream(lines[i]); diff --git a/applications/admin/static/codemirror/addon/search/search.js b/applications/admin/static/codemirror/addon/search/search.js index 71ed75b5..049f72f3 100644 --- a/applications/admin/static/codemirror/addon/search/search.js +++ b/applications/admin/static/codemirror/addon/search/search.js @@ -7,7 +7,15 @@ // Ctrl-G. (function() { - function searchOverlay(query) { + function searchOverlay(query, caseInsensitive) { + var startChar; + if (typeof query == "string") { + startChar = query.charAt(0); + query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), + caseInsensitive ? "i" : ""); + } else { + query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : ""); + } if (typeof query == "string") return {token: function(stream) { if (stream.match(query)) return "searching"; stream.next(); @@ -17,6 +25,8 @@ if (stream.match(query)) return "searching"; while (!stream.eol()) { stream.next(); + if (startChar) + stream.skipTo(startChar) || stream.skipToEnd(); if (stream.match(query, false)) break; } }}; @@ -29,13 +39,16 @@ function getSearchState(cm) { return cm.state.search || (cm.state.search = new SearchState()); } + function queryCaseInsensitive(query) { + return typeof query == "string" && query == query.toLowerCase(); + } function getSearchCursor(cm, query, pos) { // Heuristic: if the query string is all lowercase, do a case insensitive search. - return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase()); + return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); } - function dialog(cm, text, shortText, f) { - if (cm.openDialog) cm.openDialog(text, f); - else f(prompt(shortText, "")); + function dialog(cm, text, shortText, deflt, f) { + if (cm.openDialog) cm.openDialog(text, f, {value: deflt}); + else f(prompt(shortText, deflt)); } function confirmDialog(cm, text, shortText, fs) { if (cm.openConfirm) cm.openConfirm(text, fs); @@ -50,11 +63,11 @@ function doSearch(cm, rev) { var state = getSearchState(cm); if (state.query) return findNext(cm, rev); - dialog(cm, queryDialog, "Search for:", function(query) { + dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) { cm.operation(function() { if (!query || state.query) return; state.query = parseQuery(query); - cm.removeOverlay(state.overlay); + cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); state.overlay = searchOverlay(state.query); cm.addOverlay(state.overlay); state.posFrom = state.posTo = cm.getCursor(); @@ -85,10 +98,10 @@ var replacementQueryDialog = 'With: '; var doReplaceConfirm = "Replace? "; function replace(cm, all) { - dialog(cm, replaceQueryDialog, "Replace:", function(query) { + dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { if (!query) return; query = parseQuery(query); - dialog(cm, replacementQueryDialog, "Replace with:", function(text) { + dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { if (all) { cm.operation(function() { for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { diff --git a/applications/admin/static/codemirror/addon/search/searchcursor.js b/applications/admin/static/codemirror/addon/search/searchcursor.js index c034d586..711cf4ce 100644 --- a/applications/admin/static/codemirror/addon/search/searchcursor.js +++ b/applications/admin/static/codemirror/addon/search/searchcursor.js @@ -47,6 +47,7 @@ match: match}; }; } else { // String query + var origQuery = query; if (caseFold) query = query.toLowerCase(); var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; var target = query.split("\n"); @@ -58,33 +59,45 @@ this.matches = function() {}; } else { this.matches = function(reverse, pos) { - var line = fold(doc.getLine(pos.line)), len = query.length, match; - if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) - : (match = line.indexOf(query, pos.ch)) != -1) - return {from: Pos(pos.line, match), - to: Pos(pos.line, match + len)}; + if (reverse) { + var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig); + var match = line.lastIndexOf(query); + if (match > -1) { + match = adjustPos(orig, line, match); + return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; + } + } else { + var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig); + var match = line.indexOf(query); + if (match > -1) { + match = adjustPos(orig, line, match) + pos.ch; + return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; + } + } }; } } else { + var origTarget = origQuery.split("\n"); this.matches = function(reverse, pos) { - var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln)); - var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); - if (reverse ? offsetA > pos.ch || offsetA != match.length - : offsetA < pos.ch || offsetA != line.length - match.length) - return; - for (;;) { - if (reverse ? !ln : ln == doc.lineCount() - 1) return; - line = fold(doc.getLine(ln += reverse ? -1 : 1)); - match = target[reverse ? --idx : ++idx]; - if (idx > 0 && idx < target.length - 1) { - if (line != match) return; - else continue; - } - var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); - if (reverse ? offsetB != line.length - match.length : offsetB != match.length) - return; - var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); - return {from: reverse ? end : start, to: reverse ? start : end}; + var last = target.length - 1; + if (reverse) { + if (pos.line - (target.length - 1) < doc.firstLine()) return; + if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return; + var to = Pos(pos.line, origTarget[last].length); + for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln) + if (target[i] != fold(doc.getLine(ln))) return; + var line = doc.getLine(ln), cut = line.length - origTarget[0].length; + if (fold(line.slice(cut)) != target[0]) return; + return {from: Pos(ln, cut), to: to}; + } else { + if (pos.line + (target.length - 1) > doc.lastLine()) return; + var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length; + if (fold(line.slice(cut)) != target[0]) return; + var from = Pos(pos.line, cut); + for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln) + if (target[i] != fold(doc.getLine(ln))) return; + if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return; + return {from: from, to: Pos(ln, origTarget[last].length)}; } }; } @@ -106,7 +119,6 @@ for (;;) { if (this.pos = this.matches(reverse, pos)) { - if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); } this.atOccurrence = true; return this.pos.match || true; } @@ -134,6 +146,18 @@ } }; + // Maps a position in a case-folded line back to a position in the original line + // (compensating for codepoints increasing in number during folding) + function adjustPos(orig, folded, pos) { + if (orig.length == folded.length) return pos; + for (var pos1 = Math.min(pos, orig.length);;) { + var len1 = orig.slice(0, pos1).toLowerCase().length; + if (len1 < pos) ++pos1; + else if (len1 > pos) --pos1; + else return pos1; + } + } + CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { return new SearchCursor(this.doc, query, pos, caseFold); }); diff --git a/applications/admin/static/codemirror/addon/selection/active-line.js b/applications/admin/static/codemirror/addon/selection/active-line.js index e5050865..7cf7793c 100644 --- a/applications/admin/static/codemirror/addon/selection/active-line.js +++ b/applications/admin/static/codemirror/addon/selection/active-line.js @@ -12,10 +12,10 @@ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - updateActiveLine(cm); - cm.on("cursorActivity", updateActiveLine); + updateActiveLine(cm, cm.getCursor().line); + cm.on("beforeSelectionChange", selectionChange); } else if (!val && prev) { - cm.off("cursorActivity", updateActiveLine); + cm.off("beforeSelectionChange", selectionChange); clearActiveLine(cm); delete cm.state.activeLine; } @@ -28,12 +28,18 @@ } } - function updateActiveLine(cm) { - var line = cm.getLineHandleVisualStart(cm.getCursor().line); + function updateActiveLine(cm, selectedLine) { + var line = cm.getLineHandleVisualStart(selectedLine); if (cm.state.activeLine == line) return; - clearActiveLine(cm); - cm.addLineClass(line, "wrap", WRAP_CLASS); - cm.addLineClass(line, "background", BACK_CLASS); - cm.state.activeLine = line; + cm.operation(function() { + clearActiveLine(cm); + cm.addLineClass(line, "wrap", WRAP_CLASS); + cm.addLineClass(line, "background", BACK_CLASS); + cm.state.activeLine = line; + }); + } + + function selectionChange(cm, sel) { + updateActiveLine(cm, sel.head.line); } })(); diff --git a/applications/admin/static/codemirror/keymap/emacs.js b/applications/admin/static/codemirror/keymap/emacs.js index 7a3dfb11..592238bc 100644 --- a/applications/admin/static/codemirror/keymap/emacs.js +++ b/applications/admin/static/codemirror/keymap/emacs.js @@ -201,6 +201,11 @@ cm.on("change", function() { cm.setExtending(false); }); } + function clearMark(cm) { + cm.setExtending(false); + cm.setCursor(cm.getCursor()); + } + function getInput(cm, msg, f) { if (cm.openDialog) cm.openDialog(msg + ": ", f, {bottom: true}); @@ -234,6 +239,11 @@ } } + function quit(cm) { + cm.execCommand("clearSearch"); + clearMark(cm); + } + // Actual keymap var keyMap = CodeMirror.keyMap.emacs = { @@ -249,6 +259,7 @@ }), "Alt-W": function(cm) { addToRing(cm.getSelection()); + clearMark(cm); }, "Ctrl-Y": function(cm) { var start = cm.getCursor(); @@ -334,7 +345,7 @@ "Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"), "Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"), "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd", - "Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace", + "Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace", "Alt-/": "autocomplete", "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto", diff --git a/applications/admin/static/codemirror/keymap/vim.js b/applications/admin/static/codemirror/keymap/vim.js index e67a46ed..75b4e454 100644 --- a/applications/admin/static/codemirror/keymap/vim.js +++ b/applications/admin/static/codemirror/keymap/vim.js @@ -84,6 +84,7 @@ { keys: [''], type: 'keyToKey', toKeys: ['$'] }, { keys: [''], type: 'keyToKey', toKeys: [''] }, { keys: [''], type: 'keyToKey', toKeys: [''] }, + { keys: [''], type: 'keyToKey', toKeys: ['j', '^'], context: 'normal' }, // Motions { keys: ['H'], type: 'motion', motion: 'moveToTopLine', @@ -247,6 +248,12 @@ actionArgs: { forward: true }}, { keys: [''], type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }}, + { keys: [''], type: 'action', + action: 'scroll', + actionArgs: { forward: true, linewise: true }}, + { keys: [''], type: 'action', + action: 'scroll', + actionArgs: { forward: false, linewise: true }}, { keys: ['a'], type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }}, { keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true, @@ -324,12 +331,16 @@ CodeMirror.defineOption('vimMode', false, function(cm, val) { if (val) { cm.setOption('keyMap', 'vim'); + cm.setOption('disableInput', true); CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); cm.on('beforeSelectionChange', beforeSelectionChange); maybeInitVimState(cm); + CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm)); } else if (cm.state.vim) { cm.setOption('keyMap', 'default'); + cm.setOption('disableInput', false); cm.off('beforeSelectionChange', beforeSelectionChange); + CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm)); cm.state.vim = null; } }); @@ -342,6 +353,18 @@ head.ch--; } } + function getOnPasteFn(cm) { + var vim = cm.state.vim; + if (!vim.onPasteFn) { + vim.onPasteFn = function() { + if (!vim.insertMode) { + cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); + actions.enterInsertMode(cm, {}, vim); + } + }; + } + return vim.onPasteFn; + } var numberRegex = /[\d]/; var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)]; @@ -549,9 +572,9 @@ maybeInitVimState_: maybeInitVimState, InsertModeKey: InsertModeKey, - map: function(lhs, rhs) { + map: function(lhs, rhs, ctx) { // Add user defined key bindings. - exCommandDispatcher.map(lhs, rhs); + exCommandDispatcher.map(lhs, rhs, ctx); }, defineEx: function(name, prefix, func){ if (name.indexOf(prefix) !== 0) { @@ -833,11 +856,17 @@ } else { // Find the best match in the list of matchedCommands. var context = vim.visualMode ? 'visual' : 'normal'; - var bestMatch = matchedCommands[0]; // Default to first in the list. + var bestMatch; // Default to first in the list. for (var i = 0; i < matchedCommands.length; i++) { - if (matchedCommands[i].context == context) { - bestMatch = matchedCommands[i]; + var current = matchedCommands[i]; + if (current.context == context) { + bestMatch = current; break; + } else if (!bestMatch && !current.context) { + // Only set an imperfect match to best match if no best match is + // set and the imperfect match is not restricted to another + // context. + bestMatch = current; } } return getFullyMatchedCommandOrNull(bestMatch); @@ -1636,6 +1665,43 @@ markPos = markPos ? markPos : cm.getCursor(); cm.setCursor(markPos); }, + scroll: function(cm, actionArgs, vim) { + if (vim.visualMode) { + return; + } + var repeat = actionArgs.repeat || 1; + var lineHeight = cm.defaultTextHeight(); + var top = cm.getScrollInfo().top; + var delta = lineHeight * repeat; + var newPos = actionArgs.forward ? top + delta : top - delta; + var cursor = cm.getCursor(); + var cursorCoords = cm.charCoords(cursor, 'local'); + if (actionArgs.forward) { + if (newPos > cursorCoords.top) { + cursor.line += (newPos - cursorCoords.top) / lineHeight; + cursor.line = Math.ceil(cursor.line); + cm.setCursor(cursor); + cursorCoords = cm.charCoords(cursor, 'local'); + cm.scrollTo(null, cursorCoords.top); + } else { + // Cursor stays within bounds. Just reposition the scroll window. + cm.scrollTo(null, newPos); + } + } else { + var newBottom = newPos + cm.getScrollInfo().clientHeight; + if (newBottom < cursorCoords.bottom) { + cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight; + cursor.line = Math.floor(cursor.line); + cm.setCursor(cursor); + cursorCoords = cm.charCoords(cursor, 'local'); + cm.scrollTo( + null, cursorCoords.bottom - cm.getScrollInfo().clientHeight); + } else { + // Cursor stays within bounds. Just reposition the scroll window. + cm.scrollTo(null, newPos); + } + } + }, scrollToCursor: function(cm, actionArgs) { var lineNum = cm.getCursor().line; var charCoords = cm.charCoords({line: lineNum, ch: 0}, 'local'); @@ -1691,6 +1757,7 @@ cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } cm.setOption('keyMap', 'vim-insert'); + cm.setOption('disableInput', false); if (actionArgs && actionArgs.replace) { // Handle Replace-mode as a special case of insert mode. cm.toggleOverwrite(true); @@ -2726,10 +2793,9 @@ return regexp; } function showConfirm(cm, text) { - if (cm.openConfirm) { - cm.openConfirm('' + text + - ' ', function() {}, - {bottom: true}); + if (cm.openNotification) { + cm.openNotification('' + text + '', + {bottom: true, duration: 5000}); } else { alert(text); } @@ -2897,14 +2963,16 @@ // pair of commands that have a shared prefix, at least one of their // shortNames must not match the prefix of the other command. var defaultExCommandMap = [ - { name: 'map', type: 'builtIn' }, - { name: 'write', shortName: 'w', type: 'builtIn' }, - { name: 'undo', shortName: 'u', type: 'builtIn' }, - { name: 'redo', shortName: 'red', type: 'builtIn' }, - { name: 'sort', shortName: 'sor', type: 'builtIn'}, - { name: 'substitute', shortName: 's', type: 'builtIn'}, - { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'}, - { name: 'delmarks', shortName: 'delm', type: 'builtin'} + { name: 'map' }, + { name: 'nmap', shortName: 'nm' }, + { name: 'vmap', shortName: 'vm' }, + { name: 'write', shortName: 'w' }, + { name: 'undo', shortName: 'u' }, + { name: 'redo', shortName: 'red' }, + { name: 'sort', shortName: 'sor' }, + { name: 'substitute', shortName: 's' }, + { name: 'nohlsearch', shortName: 'noh' }, + { name: 'delmarks', shortName: 'delm' } ]; Vim.ExCommandDispatcher = function() { this.buildCommandMap_(); @@ -2956,6 +3024,7 @@ exCommands[commandName](cm, params); } catch(e) { showConfirm(cm, e); + throw e; } }, parseInput_: function(cm, inputStream, result) { @@ -3038,8 +3107,9 @@ this.commandMap_[key] = command; } }, - map: function(lhs, rhs) { + map: function(lhs, rhs, ctx) { if (lhs != ':' && lhs.charAt(0) == ':') { + if (ctx) { throw Error('Mode not supported for ex mappings'); } var commandName = lhs.substring(1); if (rhs != ':' && rhs.charAt(0) == ':') { // Ex to Ex mapping @@ -3059,17 +3129,21 @@ } else { if (rhs != ':' && rhs.charAt(0) == ':') { // Key to Ex mapping. - defaultKeymap.unshift({ + var mapping = { keys: parseKeyString(lhs), type: 'keyToEx', - exArgs: { input: rhs.substring(1) }}); + exArgs: { input: rhs.substring(1) }}; + if (ctx) { mapping.context = ctx; } + defaultKeymap.unshift(mapping); } else { // Key to key mapping - defaultKeymap.unshift({ + var mapping = { keys: parseKeyString(lhs), type: 'keyToKey', toKeys: parseKeyString(rhs) - }); + }; + if (ctx) { mapping.context = ctx; } + defaultKeymap.unshift(mapping); } } } @@ -3091,7 +3165,7 @@ } var exCommands = { - map: function(cm, params) { + map: function(cm, params, ctx) { var mapArgs = params.args; if (!mapArgs || mapArgs.length < 2) { if (cm) { @@ -3099,8 +3173,10 @@ } return; } - exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm); + exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx); }, + nmap: function(cm, params) { this.map(cm, params, 'normal'); }, + vmap: function(cm, params) { this.map(cm, params, 'visual'); }, move: function(cm, params) { commandDispatcher.processCommand(cm, cm.state.vim, { type: 'motion', @@ -3116,7 +3192,7 @@ var args = new CodeMirror.StringStream(params.argString); if (args.eat('!')) { reverse = true; } if (args.eol()) { return; } - if (!args.eatSpace()) { throw new Error('invalid arguments ' + args.match(/.*/)[0]); } + if (!args.eatSpace()) { return 'Invalid arguments'; } var opts = args.match(/[a-z]+/); if (opts) { opts = opts[0]; @@ -3125,13 +3201,17 @@ var decimal = opts.indexOf('d') != -1 && 1; var hex = opts.indexOf('x') != -1 && 1; var octal = opts.indexOf('o') != -1 && 1; - if (decimal + hex + octal > 1) { throw new Error('invalid arguments'); } + if (decimal + hex + octal > 1) { return 'Invalid arguments'; } number = decimal && 'decimal' || hex && 'hex' || octal && 'octal'; } - if (args.eatSpace() && args.match(/\/.*\//)) { throw new Error('patterns not supported'); } + if (args.eatSpace() && args.match(/\/.*\//)) { 'patterns not supported'; } } } - parseArgs(); + var err = parseArgs(); + if (err) { + showConfirm(cm, err + ': ' + params.argString); + return; + } var lineStart = params.line || cm.firstLine(); var lineEnd = params.lineEnd || params.line || cm.lastLine(); if (lineStart == lineEnd) { return; } @@ -3252,13 +3332,13 @@ clearSearchHighlight(cm); }, delmarks: function(cm, params) { - if (!params.argString || !params.argString.trim()) { + if (!params.argString || !trim(params.argString)) { showConfirm(cm, 'Argument required'); return; } var state = cm.state.vim; - var stream = new CodeMirror.StringStream(params.argString.trim()); + var stream = new CodeMirror.StringStream(trim(params.argString)); while (!stream.eol()) { stream.eatSpace(); @@ -3395,7 +3475,8 @@ // Actually do replace. next(); if (done) { - throw new Error('No matches for ' + query.source); + showConfirm(cm, 'No matches for ' + query.source); + return; } if (!confirm) { replaceAll(); @@ -3446,7 +3527,6 @@ var cmToVimKeymap = { 'nofallthrough': true, - 'disableInput': true, 'style': 'fat-cursor' }; function bindKeys(keys, modifier) { @@ -3493,6 +3573,7 @@ cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true); vim.insertMode = false; cm.setOption('keyMap', 'vim'); + cm.setOption('disableInput', true); cm.toggleOverwrite(false); // exit replace mode if we were in it. CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); } diff --git a/applications/admin/static/codemirror/lib/codemirror.js b/applications/admin/static/codemirror/lib/codemirror.js index 7b48bf5f..d82bd143 100644 --- a/applications/admin/static/codemirror/lib/codemirror.js +++ b/applications/admin/static/codemirror/lib/codemirror.js @@ -1,4 +1,4 @@ -// CodeMirror version 3.19 +// CodeMirror version 3.21 // // CodeMirror is the only global var we claim window.CodeMirror = (function() { @@ -9,9 +9,14 @@ window.CodeMirror = (function() { // Crude, but necessary to handle a number of hard-to-feature-detect // bugs and behavior differences. var gecko = /gecko\/\d/i.test(navigator.userAgent); - var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8); - var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); + // IE11 currently doesn't count as 'ie', since it has almost none of + // the same bugs as earlier versions. Use ie_gt10 to handle + // incompatibilities in that version. + var old_ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt8 = old_ie && (document.documentMode == null || document.documentMode < 8); + var ie_lt9 = old_ie && (document.documentMode == null || document.documentMode < 9); + var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent); + var ie = old_ie || ie_gt10; var webkit = /WebKit\//.test(navigator.userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); var chrome = /Chrome\//.test(navigator.userAgent); @@ -33,7 +38,7 @@ window.CodeMirror = (function() { if (opera_version && opera_version >= 15) { opera = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11)); - var captureMiddleClick = gecko || (ie && !ie_lt9); + var captureMiddleClick = gecko || (old_ie && !ie_lt9); // Optimize some code when these features are not used var sawReadOnlySpans = false, sawCollapsedSpans = false; @@ -59,7 +64,8 @@ window.CodeMirror = (function() { overlays: [], modeGen: 0, overwrite: false, focused: false, - suppressEdits: false, pasteIncoming: false, + suppressEdits: false, + pasteIncoming: false, cutIncoming: false, draggingText: false, highlight: new Delayed()}; @@ -73,7 +79,7 @@ window.CodeMirror = (function() { // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload - if (ie) setTimeout(bind(resetInput, this, true), 20); + if (old_ie) setTimeout(bind(resetInput, this, true), 20); registerEventHandlers(this); // IE throws unspecified error in certain cases, when @@ -193,6 +199,10 @@ window.CodeMirror = (function() { function loadMode(cm) { cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { cm.doc.iter(function(line) { if (line.stateAfter) line.stateAfter = null; if (line.styles) line.styles = null; @@ -242,7 +252,6 @@ window.CodeMirror = (function() { var map = keyMap[cm.options.keyMap], style = map.style; cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + (style ? " cm-keymap-" + style : ""); - cm.state.disableInput = map.disableInput; } function themeChanged(cm) { @@ -448,7 +457,7 @@ window.CodeMirror = (function() { // updates. function updateDisplayInner(cm, changes, visible, forced) { var display = cm.display, doc = cm.doc; - if (!display.wrapper.clientWidth) { + if (!display.wrapper.offsetWidth) { display.showingFrom = display.showingTo = doc.first; display.viewOffset = 0; return; @@ -533,6 +542,7 @@ window.CodeMirror = (function() { } display.showingFrom = from; display.showingTo = to; + display.gutters.style.height = ""; updateHeightsInViewport(cm); updateViewOffset(cm); @@ -715,9 +725,9 @@ window.CodeMirror = (function() { if (bgClass) wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild); if (cm.options.lineNumbers || markers) { - var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " + + var gutterWrap = wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"), - wrap.firstChild); + lineElement); if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap); if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) wrap.lineNumber = gutterWrap.appendChild( @@ -908,7 +918,7 @@ window.CodeMirror = (function() { doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) { if (doc.frontier >= cm.display.showingFrom) { // Visible var oldStyles = line.styles; - line.styles = highlightLine(cm, line, state); + line.styles = highlightLine(cm, line, state, true); var ischange = !oldStyles || oldStyles.length != line.styles.length; for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]; if (ischange) { @@ -917,7 +927,7 @@ window.CodeMirror = (function() { } line.stateAfter = copyState(doc.mode, state); } else { - processLine(cm, line, state); + processLine(cm, line.text, state); line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; } ++doc.frontier; @@ -961,7 +971,7 @@ window.CodeMirror = (function() { if (!state) state = startState(doc.mode); else state = copyState(doc.mode, state); doc.iter(pos, n, function(line) { - processLine(cm, line, state); + processLine(cm, line.text, state); var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo; line.stateAfter = save ? copyState(doc.mode, state) : null; ++pos; @@ -1051,7 +1061,7 @@ window.CodeMirror = (function() { // doesn't work when wrapping is on, but in that case the // situation is slightly better, since IE does cache line-wrapping // information and only recomputes per-line. - if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) { + if (old_ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) { var fragment = document.createDocumentFragment(); var chunk = 10, n = pre.childNodes.length; for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) { @@ -1111,7 +1121,7 @@ window.CodeMirror = (function() { } } if (!rect) rect = data[i] = measureRect(getRect(node)); - if (cur.measureRight) rect.right = getRect(cur.measureRight).left; + if (cur.measureRight) rect.right = getRect(cur.measureRight).left - outer.left; if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide)); } removeChildren(cm.display.measure); @@ -1287,7 +1297,7 @@ window.CodeMirror = (function() { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { var ch = x < fromX || x - fromX <= toX - x ? from : to; var xDiff = x - (ch == from ? fromX : toX); - while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; + while (isExtendingChar(lineObj.text.charAt(ch))) ++ch; var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside, xDiff < 0 ? -1 : xDiff ? 1 : 0); return pos; @@ -1385,8 +1395,10 @@ window.CodeMirror = (function() { } if (!updated && op.selectionChanged) updateSelection(cm); if (op.updateScrollPos) { - display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop; - display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft; + var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop)); + var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft)); + display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top; + display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left; alignHorizontally(cm); if (op.scrollToPos) scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from), @@ -1477,7 +1489,7 @@ window.CodeMirror = (function() { // supported or compatible enough yet to rely on.) function readInput(cm) { var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel; - if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false; + if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false; if (cm.state.pasteIncoming && cm.state.fakedLastChar) { input.value = input.value.substring(0, input.value.length - 1); cm.state.fakedLastChar = false; @@ -1495,22 +1507,32 @@ window.CodeMirror = (function() { var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; var from = sel.from, to = sel.to; + var inserted = text.slice(same); if (same < prevInput.length) from = Pos(from.line, from.ch - (prevInput.length - same)); else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming) - to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same))); + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + inserted.length)); var updateInput = cm.curOp.updateInput; - var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)), - origin: cm.state.pasteIncoming ? "paste" : "+input"}; + var changeEvent = {from: from, to: to, text: splitLines(inserted), + origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; makeChange(cm.doc, changeEvent, "end"); cm.curOp.updateInput = updateInput; signalLater(cm, "inputRead", cm, changeEvent); + if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && + cm.options.smartIndent && sel.head.ch < 100) { + var electric = cm.getModeAt(sel.head).electricChars; + if (electric) for (var i = 0; i < electric.length; i++) + if (inserted.indexOf(electric.charAt(i)) > -1) { + indentLine(cm, sel.head.line, "smart"); + break; + } + } if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = ""; else cm.display.prevInput = text; if (withOp) endOperation(cm); - cm.state.pasteIncoming = false; + cm.state.pasteIncoming = cm.state.cutIncoming = false; return true; } @@ -1545,7 +1567,7 @@ window.CodeMirror = (function() { function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); - if (ie) + if (old_ie) on(d.scroller, "dblclick", operation(cm, function(e) { if (signalDOMEvent(cm, e)) return; var pos = posFromMouse(cm, e); @@ -1651,21 +1673,22 @@ window.CodeMirror = (function() { fastPoll(cm); }); - function prepareCopy() { + function prepareCopy(e) { if (d.inaccurateSelection) { d.prevInput = ""; d.inaccurateSelection = false; d.input.value = cm.getSelection(); selectInput(d.input); } + if (e.type == "cut") cm.state.cutIncoming = true; } on(d.input, "cut", prepareCopy); on(d.input, "copy", prepareCopy); // Needed to handle Tab key in KHTML if (khtml) on(d.sizer, "mouseup", function() { - if (document.activeElement == d.input) d.input.blur(); - focusInput(cm); + if (document.activeElement == d.input) d.input.blur(); + focusInput(cm); }); } @@ -1749,6 +1772,9 @@ window.CodeMirror = (function() { e_preventDefault(e2); extendSelection(cm.doc, start); focusInput(cm); + // Work around unexplainable focus problem in IE9 (#2127) + if (old_ie && !ie_lt9) + setTimeout(function() {document.body.focus(); focusInput(cm);}, 20); } }); // Let the drag handler handle this. @@ -1823,7 +1849,7 @@ window.CodeMirror = (function() { } var move = operation(cm, function(e) { - if (!ie && !e_button(e)) done(e); + if (!old_ie && !e_button(e)) done(e); else extend(e); }); var up = operation(cm, done); @@ -1905,7 +1931,6 @@ window.CodeMirror = (function() { if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste"); cm.replaceSelection(text, null, "paste"); focusInput(cm); - onFocus(cm); } } catch(e){} @@ -1969,7 +1994,7 @@ window.CodeMirror = (function() { // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). - if (ie) wheelPixelsPerUnit = -.53; + if (old_ie) wheelPixelsPerUnit = -.53; else if (gecko) wheelPixelsPerUnit = 15; else if (chrome) wheelPixelsPerUnit = -.7; else if (safari) wheelPixelsPerUnit = -1/3; @@ -2123,7 +2148,7 @@ window.CodeMirror = (function() { var cm = this; if (!cm.state.focused) onFocus(cm); if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - if (ie && e.keyCode == 27) e.returnValue = false; + if (old_ie && e.keyCode == 27) e.returnValue = false; var code = e.keyCode; // IE does strange things with escape. cm.doc.sel.shift = code == 16 || e.shiftKey; @@ -2144,10 +2169,6 @@ window.CodeMirror = (function() { if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (this.options.electricChars && this.doc.mode.electricChars && - this.options.smartIndent && !isReadOnly(this) && - this.doc.mode.electricChars.indexOf(ch) > -1) - setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75); if (handleCharBinding(cm, e, ch)) return; if (ie && !ie_lt9) cm.display.inputHasSelection = null; fastPoll(cm); @@ -2196,7 +2217,7 @@ window.CodeMirror = (function() { var oldCSS = display.input.style.cssText; display.inputDiv.style.position = "absolute"; display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: transparent; outline: none;" + "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);"; focusInput(cm); resetInput(cm, true); @@ -2218,10 +2239,10 @@ window.CodeMirror = (function() { // Try to detect the user choosing select-all if (display.input.selectionStart != null) { - if (!ie || ie_lt9) prepareSelectAllHack(); + if (!old_ie || ie_lt9) prepareSelectAllHack(); clearTimeout(detectingSelectAll); var i = 0, poll = function(){ - if (display.prevInput == " " && display.input.selectionStart == 0) + if (display.prevInput == "\u200b" && display.input.selectionStart == 0) operation(cm, commands.selectAll)(cm); else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); else resetInput(cm); @@ -2230,7 +2251,7 @@ window.CodeMirror = (function() { } } - if (ie && !ie_lt9) prepareSelectAllHack(); + if (old_ie && !ie_lt9) prepareSelectAllHack(); if (captureMiddleClick) { e_stop(e); var mouseup = function() { @@ -2503,6 +2524,7 @@ window.CodeMirror = (function() { function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} + function cmp(a, b) {return a.line - b.line || a.ch - b.ch;} function copyPos(x) {return Pos(x.line, x.ch);} // SELECTION @@ -2647,14 +2669,13 @@ window.CodeMirror = (function() { if (coords.top + box.top < 0) doScroll = true; else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; if (doScroll != null && !phantom) { - var hidden = display.cursor.style.display == "none"; - if (hidden) { - display.cursor.style.display = ""; - display.cursor.style.left = coords.left + "px"; - display.cursor.style.top = (coords.top - display.viewOffset) + "px"; - } - display.cursor.scrollIntoView(doScroll); - if (hidden) display.cursor.style.display = "none"; + var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + + (coords.top - display.viewOffset) + "px; height: " + + (coords.bottom - coords.top + scrollerCutOff) + "px; left: " + + coords.left + "px; width: 2px;"); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); } } @@ -2737,7 +2758,10 @@ window.CodeMirror = (function() { var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); var curSpaceString = line.text.match(/^\s*/)[0], indentation; - if (how == "smart") { + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass) { if (!aggressive) return; @@ -2763,6 +2787,8 @@ window.CodeMirror = (function() { if (indentString != curSpaceString) replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + else if (doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length) + setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1); line.stateAfter = null; } @@ -2863,7 +2889,7 @@ window.CodeMirror = (function() { CodeMirror.prototype = { constructor: CodeMirror, - focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, + focus: function(){window.focus(); focusInput(this); fastPoll(this);}, setOption: function(option, value) { var options = this.options, old = options[option]; @@ -2917,7 +2943,7 @@ window.CodeMirror = (function() { }), indentSelection: operation(null, function(how) { var sel = this.doc.sel; - if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how); + if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how, true); var e = sel.to.line - (sel.to.ch ? 0 : 1); for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how); }), @@ -2962,11 +2988,31 @@ window.CodeMirror = (function() { }, getHelper: function(pos, type) { - if (!helpers.hasOwnProperty(type)) return; + return this.getHelpers(pos, type)[0]; + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) return helpers; var help = helpers[type], mode = this.getModeAt(pos); - return mode[type] && help[mode[type]] || - mode.helperType && help[mode.helperType] || - help[mode.name]; + if (typeof mode[type] == "string") { + if (help[mode[type]]) found.push(help[mode[type]]); + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) found.push(val); + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i = 0; i < help._global.length; i++) { + var cur = help._global[i]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + found.push(cur.val); + } + return found; }, getStateAfter: function(line, precise) { @@ -3113,7 +3159,10 @@ window.CodeMirror = (function() { triggerOnKeyDown: operation(null, onKeyDown), - execCommand: function(cmd) {return commands[cmd](this);}, + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + return commands[cmd](this); + }, findPosH: function(from, amount, unit, visually) { var dir = 1; @@ -3155,14 +3204,18 @@ window.CodeMirror = (function() { }, moveV: operation(null, function(dir, unit) { - var sel = this.doc.sel; - var pos = cursorCoords(this, sel.head, "div"); - if (sel.goalColumn != null) pos.left = sel.goalColumn; - var target = findPosV(this, pos, dir, unit); - - if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top); + var sel = this.doc.sel, target, goal; + if (sel.shift || sel.extend || posEq(sel.from, sel.to)) { + var pos = cursorCoords(this, sel.head, "div"); + if (sel.goalColumn != null) pos.left = sel.goalColumn; + target = findPosV(this, pos, dir, unit); + if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top); + goal = pos.left; + } else { + target = dir < 0 ? sel.from : sel.to; + } extendSelection(this.doc, target, target, dir); - sel.goalColumn = pos.left; + if (goal != null) sel.goalColumn = goal; }), toggleOverwrite: function(value) { @@ -3272,12 +3325,18 @@ window.CodeMirror = (function() { option("indentWithTabs", false); option("smartIndent", true); option("tabSize", 4, function(cm) { - loadMode(cm); + resetModeState(cm); clearCaches(cm); regChange(cm); }, true); + option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) { + cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + cm.refresh(); + }, true); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true); option("electricChars", true); option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); option("theme", "default", function(cm) { themeChanged(cm); @@ -3310,9 +3369,16 @@ window.CodeMirror = (function() { option("resetSelectionOnContextMenu", true); option("readOnly", false, function(cm, val) { - if (val == "nocursor") {onBlur(cm); cm.display.input.blur();} - else if (!val) resetInput(cm, true); + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + cm.display.disabled = true; + } else { + cm.display.disabled = false; + if (!val) resetInput(cm, true); + } }); + option("disableInput", false, function(cm, val) {if (!val) resetInput(cm, true);}, true); option("dragDrop", true); option("cursorBlinkRate", 530); @@ -3320,12 +3386,13 @@ window.CodeMirror = (function() { option("cursorHeight", 1); option("workTime", 100); option("workDelay", 100); - option("flattenSpans", true); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); option("pollInterval", 100); option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); option("historyEventDelay", 500); option("viewportMargin", 10, function(cm){cm.refresh();}, true); - option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true); + option("maxHighlightLength", 10000, resetModeState, true); option("crudeMeasuringFrom", 10000); option("moveInputWithCursor", true, function(cm, val) { if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0; @@ -3382,6 +3449,9 @@ window.CodeMirror = (function() { } } 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]; return modeObj; }; @@ -3412,9 +3482,13 @@ window.CodeMirror = (function() { var helpers = CodeMirror.helpers = {}; CodeMirror.registerHelper = function(type, name, value) { - if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {}; + if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}; helpers[type][name] = value; }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; // UTILITIES @@ -3519,7 +3593,9 @@ window.CodeMirror = (function() { indentAuto: function(cm) {cm.indentSelection("smart");}, indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, - insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");}, + insertTab: function(cm) { + cm.replaceSelection("\t", "end", "+input"); + }, defaultTab: function(cm) { if (cm.somethingSelected()) cm.indentSelection("add"); else cm.replaceSelection("\t", "end", "+input"); @@ -3692,11 +3768,12 @@ window.CodeMirror = (function() { this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, - sol: function() {return this.pos == 0;}, + sol: function() {return this.pos == this.lineStart;}, peek: function() {return this.string.charAt(this.pos) || undefined;}, next: function() { if (this.pos < this.string.length) @@ -3729,9 +3806,12 @@ window.CodeMirror = (function() { this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); this.lastColumnPos = this.start; } - return this.lastColumnValue; + 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); }, - indentation: function() {return countColumn(this.string, null, this.tabSize);}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; @@ -3747,7 +3827,12 @@ window.CodeMirror = (function() { return match; } }, - current: function(){return this.string.slice(this.start, this.pos);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; CodeMirror.StringStream = StringStream; @@ -3799,7 +3884,7 @@ window.CodeMirror = (function() { if (withOp) endOperation(cm); }; - TextMarker.prototype.find = function() { + TextMarker.prototype.find = function(bothSides) { var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; @@ -3810,7 +3895,7 @@ window.CodeMirror = (function() { if (span.to != null) to = Pos(found, span.to); } } - if (this.type == "bookmark") return from; + if (this.type == "bookmark" && !bothSides) return from; return from && {from: from, to: to}; }; @@ -3847,37 +3932,40 @@ window.CodeMirror = (function() { } }; + var nextMarkerId = 0; + function markText(doc, from, to, options, type) { if (options && options.shared) return markTextShared(doc, from, to, options, type); if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type); var marker = new TextMarker(doc, type); - if (type == "range" && !posLess(from, to)) return marker; if (options) copyObj(options, marker); + if (posLess(to, from) || posEq(from, to) && marker.clearWhenEmpty !== false) + return marker; if (marker.replacedWith) { marker.collapsed = true; marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget"); if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true; } - if (marker.collapsed) sawCollapsedSpans = true; + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + throw new Error("Inserting collapsed marker partially overlapping an existing one"); + sawCollapsedSpans = true; + } if (marker.addToHistory) addToHistory(doc, {from: from, to: to, origin: "markText"}, {head: doc.sel.head, anchor: doc.sel.anchor}, NaN); - var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine; + var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function(line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine) updateMaxLine = true; var span = {from: null, to: null, marker: marker}; - size += line.text.length; - if (curLine == from.line) {span.from = from.ch; size -= from.ch;} - if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;} - if (marker.collapsed) { - if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch); - if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch); - else updateLineHeight(line, 0); - } + if (curLine == from.line) span.from = from.ch; + if (curLine == to.line) span.to = to.ch; + if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0); addMarkedSpan(line, span); ++curLine; }); @@ -3893,9 +3981,7 @@ window.CodeMirror = (function() { doc.clearHistory(); } if (marker.collapsed) { - if (collapsedAtStart != collapsedAtEnd) - throw new Error("Inserting collapsed marker overlapping an existing one"); - marker.size = size; + marker.id = ++nextMarkerId; marker.atomic = true; } if (cm) { @@ -3968,7 +4054,7 @@ window.CodeMirror = (function() { if (old) for (var i = 0, nw; i < old.length; ++i) { var span = old[i], marker = span.marker; var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); - if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) { + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); (nw || (nw = [])).push({from: span.from, to: endsAfter ? null : span.to, @@ -3982,7 +4068,7 @@ window.CodeMirror = (function() { if (old) for (var i = 0, nw; i < old.length; ++i) { var span = old[i], marker = span.marker; var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); - if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) { + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh, to: span.to == null ? null : span.to - endCh, @@ -4032,13 +4118,9 @@ window.CodeMirror = (function() { } } } - if (sameLine && first) { - // Make sure we didn't create any zero-length spans - for (var i = 0; i < first.length; ++i) - if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark") - first.splice(i--, 1); - if (!first.length) first = null; - } + // Make sure we didn't create any zero-length spans + if (first) first = clearEmptySpans(first); + if (last && last != first) last = clearEmptySpans(last); var newMarkers = [first]; if (!sameLine) { @@ -4055,6 +4137,16 @@ window.CodeMirror = (function() { return newMarkers; } + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + spans.splice(i--, 1); + } + if (!spans.length) return null; + return spans; + } + function mergeOldSpans(doc, change) { var old = getOldSpans(doc, change); var stretched = stretchSpansOverChange(doc, change); @@ -4105,20 +4197,48 @@ window.CodeMirror = (function() { return parts; } - function collapsedSpanAt(line, ch) { + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; } + + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) return lenDiff; + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) return -fromCmp; + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) return toCmp; + return b.id - a.id; + } + + function collapsedSpanAtSide(line, start) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) for (var sp, i = 0; i < sps.length; ++i) { sp = sps[i]; - if (!sp.marker.collapsed) continue; - if ((sp.from == null || sp.from < ch) && - (sp.to == null || sp.to > ch) && - (!found || found.width < sp.marker.width)) + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker; } return found; } - function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); } - function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); } + + 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]; + if (!sp.marker.collapsed) continue; + var found = sp.marker.find(true); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; + if (fromCmp <= 0 && (cmp(found.to, from) || extraRight(sp.marker) - extraLeft(marker)) > 0 || + fromCmp >= 0 && (cmp(found.from, to) || extraLeft(sp.marker) - extraRight(marker)) < 0) + return true; + } + } function visualLine(doc, line) { var merged; @@ -4148,6 +4268,7 @@ window.CodeMirror = (function() { for (var sp, i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to && + (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) return true; } @@ -4241,6 +4362,7 @@ window.CodeMirror = (function() { this.height = estimateHeight ? estimateHeight(this) : 1; }; eventMixin(Line); + Line.prototype.lineNo = function() { return lineNo(this); }; function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; @@ -4261,7 +4383,7 @@ window.CodeMirror = (function() { // Run the given mode's parser over a line, update the styles // array, which contains alternating fragments of text and CSS // classes. - function runMode(cm, text, mode, state, f) { + function runMode(cm, text, mode, state, f, forceToEnd) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; var curStart = 0, curStyle = null; @@ -4270,11 +4392,16 @@ window.CodeMirror = (function() { while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; + if (forceToEnd) processLine(cm, text, state, stream.pos); stream.pos = text.length; style = null; } else { style = mode.token(stream, state); } + if (cm.options.addModeClass) { + var mName = CodeMirror.innerMode(mode, state).mode.name; + if (mName) style = "m-" + (style ? mName + " " + style : mName); + } if (!flattenSpans || curStyle != style) { if (curStart < stream.start) f(stream.start, curStyle); curStart = stream.start; curStyle = style; @@ -4289,12 +4416,14 @@ window.CodeMirror = (function() { } } - function highlightLine(cm, line, state) { + function highlightLine(cm, line, state, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen]; // Compute the base array of styles - runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);}); + runMode(cm, line.text, cm.doc.mode, state, function(end, style) { + st.push(end, style); + }, forceToEnd); // Run overlays, adjust style array. for (var o = 0; o < cm.state.overlays.length; ++o) { @@ -4333,17 +4462,18 @@ window.CodeMirror = (function() { // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. - function processLine(cm, line, state) { + function processLine(cm, text, state, startAt) { var mode = cm.doc.mode; - var stream = new StringStream(line.text, cm.options.tabSize); - if (line.text == "" && mode.blankLine) mode.blankLine(state); + var stream = new StringStream(text, cm.options.tabSize); + stream.start = stream.pos = startAt || 0; + if (text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) { mode.token(stream, state); stream.start = stream.pos; } } - var styleToClassCache = {}; + var styleToClassCache = {}, styleToClassCacheWithMode = {}; function interpretTokenStyle(style, builder) { if (!style) return null; for (;;) { @@ -4356,8 +4486,9 @@ window.CodeMirror = (function() { else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop])) builder[prop] += " " + lineClass[2]; } - return styleToClassCache[style] || - (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-")); + var cache = builder.cm.options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = "cm-" + style.replace(/ +/g, " cm-")); } function buildLineContent(cm, realLine, measure, copyWidgets) { @@ -4374,7 +4505,7 @@ window.CodeMirror = (function() { builder.measure = line == realLine && measure; builder.pos = 0; builder.addToken = builder.measure ? buildTokenMeasure : buildToken; - if ((ie || webkit) && cm.getOption("lineWrapping")) + if ((old_ie || webkit) && cm.getOption("lineWrapping")) builder.addToken = buildTokenSplitSpaces(builder.addToken); var next = insertLineContent(line, builder, getLineStyles(cm, line)); if (measure && line == realLine && !builder.measuredSomething) { @@ -4411,17 +4542,23 @@ window.CodeMirror = (function() { return builder; } - var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g; + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + return token; + } + function buildToken(builder, text, style, startStyle, endStyle, title) { if (!text) return; - if (!tokenSpecialChars.test(text)) { + var special = builder.cm.options.specialChars; + if (!special.test(text)) { builder.col += text.length; var content = document.createTextNode(text); } else { var content = document.createDocumentFragment(), pos = 0; while (true) { - tokenSpecialChars.lastIndex = pos; - var m = tokenSpecialChars.exec(text); + special.lastIndex = pos; + var m = special.exec(text); var skipped = m ? m.index - pos : text.length - pos; if (skipped) { content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); @@ -4434,8 +4571,7 @@ window.CodeMirror = (function() { content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); builder.col += tabWidth; } else { - var token = elt("span", "\u2022", "cm-invalidchar"); - token.title = "\\u" + m[0].charCodeAt(0).toString(16); + var token = builder.cm.options.specialCharPlaceholder(m[0]); content.appendChild(token); builder.col += 1; } @@ -4455,13 +4591,12 @@ window.CodeMirror = (function() { function buildTokenMeasure(builder, text, style, startStyle, endStyle) { var wrapping = builder.cm.options.lineWrapping; for (var i = 0; i < text.length; ++i) { - var ch = text.charAt(i), start = i == 0; - if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) { - ch = text.slice(i, i + 2); - ++i; - } else if (i && wrapping && spanAffectsWrapping(text, i)) { + var start = i == 0, to = i + 1; + while (to < text.length && isExtendingChar(text.charAt(to))) ++to; + var ch = text.slice(i, to); + i = to - 1; + if (i && wrapping && spanAffectsWrapping(text, i)) builder.pre.appendChild(elt("wbr")); - } var old = builder.measure[builder.pos]; var span = builder.measure[builder.pos] = buildToken(builder, ch, style, @@ -4470,7 +4605,7 @@ window.CodeMirror = (function() { // In IE single-space nodes wrap differently than spaces // embedded in larger text nodes, except when set to // white-space: normal (issue #1268). - if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) && + if (old_ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) && i < text.length - 1 && !/\s/.test(text.charAt(i + 1))) span.style.whiteSpace = "normal"; builder.pos += ch.length; @@ -4538,7 +4673,7 @@ window.CodeMirror = (function() { if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; if (m.title && !title) title = m.title; - if (m.collapsed && (!collapsed || collapsed.marker.size < m.size)) + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) collapsed = sp; } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; @@ -4588,7 +4723,8 @@ window.CodeMirror = (function() { var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // First adjust the line structure - if (from.ch == 0 && to.ch == 0 && lastText == "") { + if (from.ch == 0 && to.ch == 0 && lastText == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. for (var i = 0, e = text.length - 1, added = []; i < e; ++i) @@ -4870,10 +5006,11 @@ window.CodeMirror = (function() { clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);}, markClean: function() { - this.cleanGeneration = this.changeGeneration(); + this.cleanGeneration = this.changeGeneration(true); }, - changeGeneration: function() { - this.history.lastOp = this.history.lastOrigin = null; + changeGeneration: function(forceSplit) { + if (forceSplit) + this.history.lastOp = this.history.lastOrigin = null; return this.history.generation; }, isClean: function (gen) { @@ -4895,7 +5032,8 @@ window.CodeMirror = (function() { }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), - insertLeft: options && options.insertLeft}; + insertLeft: options && options.insertLeft, + clearWhenEmpty: false}; pos = clipPos(this, pos); return markText(this, pos, pos, realOpts, "bookmark"); }, @@ -5176,10 +5314,10 @@ window.CodeMirror = (function() { anchorBefore: doc.sel.anchor, headBefore: doc.sel.head, anchorAfter: selAfter.anchor, headAfter: selAfter.head}; hist.done.push(cur); - hist.generation = ++hist.maxGeneration; while (hist.done.length > hist.undoDepth) hist.done.shift(); } + hist.generation = ++hist.maxGeneration; hist.lastTime = time; hist.lastOp = opId; hist.lastOrigin = change.origin; @@ -5475,7 +5613,8 @@ window.CodeMirror = (function() { return true; } - var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/; + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); } // DOM UTILITIES @@ -5548,7 +5687,7 @@ window.CodeMirror = (function() { if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true; if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false; } - return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1)); + return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|\u2026[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1)); }; var knownScrollbarWidth; @@ -5616,14 +5755,14 @@ window.CodeMirror = (function() { var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", - 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete", - 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", - 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home", - 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"}; + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"}; CodeMirror.keyNames = keyNames; (function() { // Number keys - for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i); + for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i); // Alphabetic keys for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); // Function keys @@ -5680,29 +5819,29 @@ window.CodeMirror = (function() { } var bidiOther; function getBidiPartAt(order, pos) { + bidiOther = null; for (var i = 0, found; i < order.length; ++i) { var cur = order[i]; - if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; } - if (cur.from == pos || cur.to == pos) { + if (cur.from < pos && cur.to > pos) return i; + if ((cur.from == pos || cur.to == pos)) { if (found == null) { found = i; } else if (compareBidiLevel(order, cur.level, order[found].level)) { - bidiOther = found; + if (cur.from != cur.to) bidiOther = found; return i; } else { - bidiOther = i; + if (cur.from != cur.to) bidiOther = i; return found; } } } - bidiOther = null; return found; } function moveInLine(line, pos, dir, byUnit) { if (!byUnit) return pos + dir; do pos += dir; - while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); + while (pos > 0 && isExtendingChar(line.text.charAt(pos))); return pos; } @@ -5737,7 +5876,7 @@ window.CodeMirror = (function() { function moveLogically(line, start, dir, byUnit) { var target = start + dir; - if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir; + if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir; return target < 0 || target > line.text.length ? null : target; } @@ -5829,7 +5968,7 @@ window.CodeMirror = (function() { if (type == ",") types[i] = "N"; else if (type == "%") { for (var end = i + 1; end < len && types[end] == "%"; ++end) {} - var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N"; + var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; for (var j = i; j < end; ++j) types[j] = replace; i = end - 1; } @@ -5854,7 +5993,7 @@ window.CodeMirror = (function() { if (isNeutral.test(types[i])) { for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} var before = (i ? types[i-1] : outerType) == "L"; - var after = (end < len - 1 ? types[end] : outerType) == "L"; + var after = (end < len ? types[end] : outerType) == "L"; var replace = before || after ? "L" : "R"; for (var j = i; j < end; ++j) types[j] = replace; i = end - 1; @@ -5904,7 +6043,7 @@ window.CodeMirror = (function() { // THE END - CodeMirror.version = "3.19.0"; + CodeMirror.version = "3.21.0"; return CodeMirror; })(); diff --git a/applications/admin/static/codemirror/mode/css/css.js b/applications/admin/static/codemirror/mode/css/css.js index f47aba75..8f6fe7df 100644 --- a/applications/admin/static/codemirror/mode/css/css.js +++ b/applications/admin/static/codemirror/mode/css/css.js @@ -3,87 +3,80 @@ CodeMirror.defineMode("css", function(config, parserConfig) { if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); - var indentUnit = config.indentUnit || config.tabSize || 2, - hooks = parserConfig.hooks || {}, - atMediaTypes = parserConfig.atMediaTypes || {}, - atMediaFeatures = parserConfig.atMediaFeatures || {}, + var indentUnit = config.indentUnit, + tokenHooks = parserConfig.tokenHooks, + mediaTypes = parserConfig.mediaTypes || {}, + mediaFeatures = parserConfig.mediaFeatures || {}, propertyKeywords = parserConfig.propertyKeywords || {}, colorKeywords = parserConfig.colorKeywords || {}, valueKeywords = parserConfig.valueKeywords || {}, - allowNested = !!parserConfig.allowNested, - type = null; + fontProperties = parserConfig.fontProperties || {}, + allowNested = parserConfig.allowNested; + var type, override; function ret(style, tp) { type = tp; return style; } + // Tokenizers + function tokenBase(stream, state) { var ch = stream.next(); - if (hooks[ch]) { - // result[0] is style and result[1] is type - var result = hooks[ch](stream, state); + if (tokenHooks[ch]) { + var result = tokenHooks[ch](stream, state); if (result !== false) return result; } - if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} - else if (ch == "=") ret(null, "compare"); - else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); - else if (ch == "\"" || ch == "'") { + if (ch == "@") { + stream.eatWhile(/[\w\\\-]/); + return ret("def", stream.current()); + } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) { + return ret(null, "compare"); + } else if (ch == "\"" || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); - } - else if (ch == "#") { + } else if (ch == "#") { stream.eatWhile(/[\w\\\-]/); return ret("atom", "hash"); - } - else if (ch == "!") { + } else if (ch == "!") { stream.match(/^\s*\w*/); return ret("keyword", "important"); - } - else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { + } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); - } - else if (ch === "-") { - if (/\d/.test(stream.peek())) { + } else if (ch === "-") { + if (/[\d.]/.test(stream.peek())) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); } else if (stream.match(/^[^-]+-/)) { return ret("meta", "meta"); } - } - else if (/[,+>*\/]/.test(ch)) { + } else if (/[,+>*\/]/.test(ch)) { return ret(null, "select-op"); - } - else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { + } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { return ret("qualifier", "qualifier"); - } - else if (ch == ":") { - return ret("operator", ch); - } - else if (/[;{}\[\]\(\)]/.test(ch)) { + } else if (/[:;{}\[\]\(\)]/.test(ch)) { return ret(null, ch); - } - else if (ch == "u" && stream.match("rl(")) { + } else if (ch == "u" && stream.match("rl(")) { stream.backUp(1); state.tokenize = tokenParenthesized; - return ret("property", "variable"); - } - else { + return ret("property", "word"); + } else if (/[\w\\\-]/.test(ch)) { stream.eatWhile(/[\w\\\-]/); - return ret("property", "variable"); + return ret("property", "word"); + } else { + return ret(null, null); } } - function tokenString(quote, nonInclusive) { + function tokenString(quote) { return function(stream, state) { var escaped = false, ch; while ((ch = stream.next()) != null) { - if (ch == quote && !escaped) + if (ch == quote && !escaped) { + if (quote == ")") stream.backUp(1); break; + } escaped = !escaped && ch == "\\"; } - if (!escaped) { - if (nonInclusive) stream.backUp(1); - state.tokenize = tokenBase; - } + if (ch == quote || !escaped && quote != ")") state.tokenize = null; return ret("string", "string"); }; } @@ -91,218 +84,238 @@ CodeMirror.defineMode("css", function(config, parserConfig) { function tokenParenthesized(stream, state) { stream.next(); // Must be '(' if (!stream.match(/\s*[\"\']/, false)) - state.tokenize = tokenString(")", true); + state.tokenize = tokenString(")"); else - state.tokenize = tokenBase; + state.tokenize = null; return ret(null, "("); } + // Context management + + function Context(type, indent, prev) { + this.type = type; + this.indent = indent; + this.prev = prev; + } + + function pushContext(state, stream, type) { + state.context = new Context(type, stream.indentation() + indentUnit, state.context); + return type; + } + + function popContext(state) { + state.context = state.context.prev; + return state.context.type; + } + + function pass(type, stream, state) { + return states[state.context.type](type, stream, state); + } + function popAndPass(type, stream, state, n) { + for (var i = n || 1; i > 0; i--) + state.context = state.context.prev; + return pass(type, stream, state); + } + + // Parser + + function wordAsValue(stream) { + var word = stream.current().toLowerCase(); + if (valueKeywords.hasOwnProperty(word)) + override = "atom"; + else if (colorKeywords.hasOwnProperty(word)) + override = "keyword"; + else + override = "variable"; + } + + var states = {}; + + states.top = function(type, stream, state) { + if (type == "{") { + return pushContext(state, stream, "block"); + } else if (type == "}" && state.context.prev) { + return popContext(state); + } else if (type == "@media") { + return pushContext(state, stream, "media"); + } else if (type == "@font-face") { + return "font_face_before"; + } else if (type && type.charAt(0) == "@") { + return pushContext(state, stream, "at"); + } else if (type == "hash") { + override = "builtin"; + } else if (type == "word") { + override = "tag"; + } else if (type == "variable-definition") { + return "maybeprop"; + } else if (type == "interpolation") { + return pushContext(state, stream, "interpolation"); + } else if (type == ":") { + return "pseudo"; + } else if (allowNested && type == "(") { + return pushContext(state, stream, "params"); + } + return state.context.type; + }; + + states.block = function(type, stream, state) { + if (type == "word") { + if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) { + override = "property"; + return "maybeprop"; + } else if (allowNested) { + override = stream.match(/^\s*:/, false) ? "property" : "tag"; + return "block"; + } else { + override += " error"; + return "maybeprop"; + } + } else if (type == "meta") { + return "block"; + } else if (!allowNested && (type == "hash" || type == "qualifier")) { + override = "error"; + return "block"; + } else { + return states.top(type, stream, state); + } + }; + + states.maybeprop = function(type, stream, state) { + if (type == ":") return pushContext(state, stream, "prop"); + return pass(type, stream, state); + }; + + states.prop = function(type, stream, state) { + if (type == ";") return popContext(state); + if (type == "{" && allowNested) return pushContext(state, stream, "propBlock"); + if (type == "}" || type == "{") return popAndPass(type, stream, state); + if (type == "(") return pushContext(state, stream, "parens"); + + if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { + override += " error"; + } else if (type == "word") { + wordAsValue(stream); + } else if (type == "interpolation") { + return pushContext(state, stream, "interpolation"); + } + return "prop"; + }; + + states.propBlock = function(type, _stream, state) { + if (type == "}") return popContext(state); + if (type == "word") { override = "property"; return "maybeprop"; } + return state.context.type; + }; + + states.parens = function(type, stream, state) { + if (type == "{" || type == "}") return popAndPass(type, stream, state); + if (type == ")") return popContext(state); + return "parens"; + }; + + states.pseudo = function(type, stream, state) { + if (type == "word") { + override = "variable-3"; + return state.context.type; + } + return pass(type, stream, state); + }; + + states.media = function(type, stream, state) { + if (type == "(") return pushContext(state, stream, "media_parens"); + if (type == "}") return popAndPass(type, stream, state); + if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top"); + + if (type == "word") { + var word = stream.current().toLowerCase(); + if (word == "only" || word == "not" || word == "and") + override = "keyword"; + else if (mediaTypes.hasOwnProperty(word)) + override = "attribute"; + else if (mediaFeatures.hasOwnProperty(word)) + override = "property"; + else + override = "error"; + } + return state.context.type; + }; + + states.media_parens = function(type, stream, state) { + if (type == ")") return popContext(state); + if (type == "{" || type == "}") return popAndPass(type, stream, state, 2); + return states.media(type, stream, state); + }; + + states.font_face_before = function(type, stream, state) { + if (type == "{") + return pushContext(state, stream, "font_face"); + return pass(type, stream, state); + }; + + states.font_face = function(type, stream, state) { + if (type == "}") return popContext(state); + if (type == "word") { + if (!fontProperties.hasOwnProperty(stream.current().toLowerCase())) + override = "error"; + else + override = "property"; + return "maybeprop"; + } + return "font_face"; + }; + + states.at = function(type, stream, state) { + if (type == ";") return popContext(state); + if (type == "{" || type == "}") return popAndPass(type, stream, state); + if (type == "word") override = "tag"; + else if (type == "hash") override = "builtin"; + return "at"; + }; + + states.interpolation = function(type, stream, state) { + if (type == "}") return popContext(state); + if (type == "{" || type == ";") return popAndPass(type, stream, state); + if (type != "variable") override = "error"; + return "interpolation"; + }; + + states.params = function(type, stream, state) { + if (type == ")") return popContext(state); + if (type == "{" || type == "}") return popAndPass(type, stream, state); + if (type == "word") wordAsValue(stream); + return "params"; + }; + return { startState: function(base) { - return {tokenize: tokenBase, - baseIndent: base || 0, - stack: [], - lastToken: null}; + return {tokenize: null, + state: "top", + context: new Context("top", base || 0, null)}; }, token: function(stream, state) { - - // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) - // - // rule** or **ruleset: - // A selector + braces combo, or an at-rule. - // - // declaration block: - // A sequence of declarations. - // - // declaration: - // A property + colon + value combo. - // - // property value: - // The entire value of a property. - // - // component value: - // A single piece of a property value. Like the 5px in - // text-shadow: 0 0 5px blue;. Can also refer to things that are - // multiple terms, like the 1-4 terms that make up the background-size - // portion of the background shorthand. - // - // term: - // The basic unit of author-facing CSS, like a single number (5), - // dimension (5px), string ("foo"), or function. Officially defined - // by the CSS 2.1 grammar (look for the 'term' production) - // - // - // simple selector: - // A single atomic selector, like a type selector, an attr selector, a - // class selector, etc. - // - // compound selector: - // One or more simple selectors without a combinator. div.example is - // compound, div > .example is not. - // - // complex selector: - // One or more compound selectors chained with combinators. - // - // combinator: - // The parts of selectors that express relationships. There are four - // currently - the space (descendant combinator), the greater-than - // bracket (child combinator), the plus sign (next sibling combinator), - // and the tilda (following sibling combinator). - // - // sequence of selectors: - // One or more of the named type of selector chained with commas. - - state.tokenize = state.tokenize || tokenBase; - if (state.tokenize == tokenBase && stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - if (style && typeof style != "string") style = ret(style[0], style[1]); - - // Changing style returned based on context - var context = state.stack[state.stack.length-1]; - if (style == "variable") { - if (type == "variable-definition") state.stack.push("propertyValue"); - return state.lastToken = "variable-2"; - } else if (style == "property") { - var word = stream.current().toLowerCase(); - if (context == "propertyValue") { - if (valueKeywords.hasOwnProperty(word)) { - style = "string-2"; - } else if (colorKeywords.hasOwnProperty(word)) { - style = "keyword"; - } else { - style = "variable-2"; - } - } else if (context == "rule") { - if (!propertyKeywords.hasOwnProperty(word)) { - style += " error"; - } - } else if (context == "block") { - // if a value is present in both property, value, or color, the order - // of preference is property -> color -> value - if (propertyKeywords.hasOwnProperty(word)) { - style = "property"; - } else if (colorKeywords.hasOwnProperty(word)) { - style = "keyword"; - } else if (valueKeywords.hasOwnProperty(word)) { - style = "string-2"; - } else { - style = "tag"; - } - } else if (!context || context == "@media{") { - style = "tag"; - } else if (context == "@media") { - if (atMediaTypes[stream.current()]) { - style = "attribute"; // Known attribute - } else if (/^(only|not)$/.test(word)) { - style = "keyword"; - } else if (word == "and") { - style = "error"; // "and" is only allowed in @mediaType - } else if (atMediaFeatures.hasOwnProperty(word)) { - style = "error"; // Known property, should be in @mediaType( - } else { - // Unknown, expecting keyword or attribute, assuming attribute - style = "attribute error"; - } - } else if (context == "@mediaType") { - if (atMediaTypes.hasOwnProperty(word)) { - style = "attribute"; - } else if (word == "and") { - style = "operator"; - } else if (/^(only|not)$/.test(word)) { - style = "error"; // Only allowed in @media - } else { - // Unknown attribute or property, but expecting property (preceded - // by "and"). Should be in parentheses - style = "error"; - } - } else if (context == "@mediaType(") { - if (propertyKeywords.hasOwnProperty(word)) { - // do nothing, remains "property" - } else if (atMediaTypes.hasOwnProperty(word)) { - style = "error"; // Known property, should be in parentheses - } else if (word == "and") { - style = "operator"; - } else if (/^(only|not)$/.test(word)) { - style = "error"; // Only allowed in @media - } else { - style += " error"; - } - } else if (context == "@import") { - style = "tag"; - } else { - style = "error"; - } - } else if (style == "atom") { - if(!context || context == "@media{" || context == "block") { - style = "builtin"; - } else if (context == "propertyValue") { - if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { - style += " error"; - } - } else { - style = "error"; - } - } else if (context == "@media" && type == "{") { - style = "error"; + if (!state.tokenize && stream.eatSpace()) return null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style && typeof style == "object") { + type = style[1]; + style = style[0]; } - - // Push/pop context stack - if (type == "{") { - if (context == "@media" || context == "@mediaType") { - state.stack[state.stack.length-1] = "@media{"; - } - else { - var newContext = allowNested ? "block" : "rule"; - state.stack.push(newContext); - } - } - else if (type == "}") { - if (context == "interpolation") style = "operator"; - // Pop off end of array until { is reached - while(state.stack.length){ - var removed = state.stack.pop(); - if(removed.indexOf("{") > -1){ - break; - } - } - } - else if (type == "interpolation") state.stack.push("interpolation"); - else if (type == "@media") state.stack.push("@media"); - else if (type == "@import") state.stack.push("@import"); - else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) - state.stack[state.stack.length-1] = "@mediaType"; - else if (context == "@mediaType" && stream.current() == ",") - state.stack[state.stack.length-1] = "@media"; - else if (type == "(") { - if (context == "@media" || context == "@mediaType") { - // Make sure @mediaType is used to avoid error on { - state.stack[state.stack.length-1] = "@mediaType"; - state.stack.push("@mediaType("); - } - else state.stack.push("("); - } - else if (type == ")") { - // Pop off end of array until ( is reached - while(state.stack.length){ - var removed = state.stack.pop(); - if(removed.indexOf("(") > -1){ - break; - } - } - } - else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue"); - else if (context == "propertyValue" && type == ";") state.stack.pop(); - else if (context == "@import" && type == ";") state.stack.pop(); - - return state.lastToken = style; + override = style; + state.state = states[state.state](type, stream, state); + return override; }, indent: function(state, textAfter) { - var n = state.stack.length; - if (/^\}/.test(textAfter)) - n -= state.stack[n-1] == "propertyValue" ? 2 : 1; - return state.baseIndent + n * indentUnit; + var cx = state.context, ch = textAfter && textAfter.charAt(0); + var indent = cx.indent; + if (cx.prev && + (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") || + ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") || + ch == "{" && (cx.type == "at" || cx.type == "media"))) { + indent = cx.indent - indentUnit; + cx = cx.prev; + } + return indent; }, electricChars: "}", @@ -321,12 +334,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return keys; } - var atMediaTypes = keySet([ + var mediaTypes_ = [ "all", "aural", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "embossed" - ]); + ], mediaTypes = keySet(mediaTypes_); - var atMediaFeatures = keySet([ + var mediaFeatures_ = [ "width", "min-width", "max-width", "height", "min-height", "max-height", "device-width", "min-device-width", "max-device-width", "device-height", "min-device-height", "max-device-height", "aspect-ratio", @@ -335,9 +348,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "max-color", "color-index", "min-color-index", "max-color-index", "monochrome", "min-monochrome", "max-monochrome", "resolution", "min-resolution", "max-resolution", "scan", "grid" - ]); + ], mediaFeatures = keySet(mediaFeatures_); - var propertyKeywords = keySet([ + var propertyKeywords_ = [ "align-content", "align-items", "align-self", "alignment-adjust", "alignment-baseline", "anchor-point", "animation", "animation-delay", "animation-direction", "animation-duration", "animation-iteration-count", @@ -425,9 +438,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode" - ]); + ], propertyKeywords = keySet(propertyKeywords_); - var colorKeywords = keySet([ + var colorKeywords_ = [ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", @@ -454,9 +467,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen" - ]); + ], colorKeywords = keySet(colorKeywords_); - var valueKeywords = keySet([ + var valueKeywords_ = [ "above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", @@ -539,7 +552,15 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", "window", "windowframe", "windowtext", "x-large", "x-small", "xor", "xx-large", "xx-small" - ]); + ], valueKeywords = keySet(valueKeywords_); + + var fontProperties_ = [ + "font-family", "src", "unicode-range", "font-variant", "font-feature-settings", + "font-stretch", "font-weight", "font-style" + ], fontProperties = keySet(fontProperties_); + + var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_); + CodeMirror.registerHelper("hintWords", "css", allWords); function tokenCComment(stream, state) { var maybeEnd = false, ch; @@ -553,67 +574,47 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return ["comment", "comment"]; } + function tokenSGMLComment(stream, state) { + if (stream.skipTo("-->")) { + stream.match("-->"); + state.tokenize = null; + } else { + stream.skipToEnd(); + } + return ["comment", "comment"]; + } + CodeMirror.defineMIME("text/css", { - atMediaTypes: atMediaTypes, - atMediaFeatures: atMediaFeatures, + mediaTypes: mediaTypes, + mediaFeatures: mediaFeatures, propertyKeywords: propertyKeywords, colorKeywords: colorKeywords, valueKeywords: valueKeywords, - hooks: { + fontProperties: fontProperties, + tokenHooks: { "<": function(stream, state) { - function tokenSGMLComment(stream, state) { - var dashes = 0, ch; - while ((ch = stream.next()) != null) { - if (dashes >= 2 && ch == ">") { - state.tokenize = null; - break; - } - dashes = (ch == "-") ? dashes + 1 : 0; - } - return ["comment", "comment"]; - } - if (stream.eat("!")) { - state.tokenize = tokenSGMLComment; - return tokenSGMLComment(stream, state); - } + if (!stream.match("!--")) return false; + state.tokenize = tokenSGMLComment; + return tokenSGMLComment(stream, state); }, "/": function(stream, state) { - if (stream.eat("*")) { - state.tokenize = tokenCComment; - return tokenCComment(stream, state); - } - return false; + if (!stream.eat("*")) return false; + state.tokenize = tokenCComment; + return tokenCComment(stream, state); } }, name: "css" }); CodeMirror.defineMIME("text/x-scss", { - atMediaTypes: atMediaTypes, - atMediaFeatures: atMediaFeatures, + mediaTypes: mediaTypes, + mediaFeatures: mediaFeatures, propertyKeywords: propertyKeywords, colorKeywords: colorKeywords, valueKeywords: valueKeywords, + fontProperties: fontProperties, allowNested: true, - hooks: { - ":": function(stream) { - if (stream.match(/\s*{/)) { - return [null, "{"]; - } - return false; - }, - "$": function(stream) { - stream.match(/^[\w-]+/); - if (stream.peek() == ":") { - return ["variable", "variable-definition"]; - } - return ["variable", "variable"]; - }, - ",": function(_stream, state) { - if (state.stack[state.stack.length - 1] == "propertyValue") { - return ["operator", ";"]; - } - }, + tokenHooks: { "/": function(stream, state) { if (stream.eat("/")) { stream.skipToEnd(); @@ -625,15 +626,58 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return ["operator", "operator"]; } }, + ":": function(stream) { + if (stream.match(/\s*{/)) + return [null, "{"]; + return false; + }, + "$": function(stream) { + stream.match(/^[\w-]+/); + if (stream.match(/^\s*:/, false)) + return ["variable-2", "variable-definition"]; + return ["variable-2", "variable"]; + }, "#": function(stream) { - if (stream.eat("{")) { - return ["operator", "interpolation"]; - } else { - stream.eatWhile(/[\w\\\-]/); - return ["atom", "hash"]; - } + if (!stream.eat("{")) return false; + return [null, "interpolation"]; } }, - name: "css" + name: "css", + helperType: "scss" + }); + + CodeMirror.defineMIME("text/x-less", { + mediaTypes: mediaTypes, + mediaFeatures: mediaFeatures, + propertyKeywords: propertyKeywords, + colorKeywords: colorKeywords, + valueKeywords: valueKeywords, + fontProperties: fontProperties, + allowNested: true, + tokenHooks: { + "/": function(stream, state) { + if (stream.eat("/")) { + stream.skipToEnd(); + return ["comment", "comment"]; + } else if (stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } else { + return ["operator", "operator"]; + } + }, + "@": function(stream) { + if (stream.match(/^(charset|document|font-face|import|keyframes|media|namespace|page|supports)\b/, false)) return false; + stream.eatWhile(/[\w\\\-]/); + if (stream.match(/^\s*:/, false)) + return ["variable-2", "variable-definition"]; + return ["variable-2", "variable"]; + }, + "&": function() { + return ["atom", "atom"]; + } + }, + name: "css", + helperType: "less" }); })(); diff --git a/applications/admin/static/codemirror/mode/css/scss.html b/applications/admin/static/codemirror/mode/css/scss.html index 72781c0d..0677d08f 100644 --- a/applications/admin/static/codemirror/mode/css/scss.html +++ b/applications/admin/static/codemirror/mode/css/scss.html @@ -150,7 +150,7 @@ code { }); -

MIME types defined: text/scss.

+

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

Parsing/Highlighting Tests: normal, verbose.

diff --git a/applications/admin/static/codemirror/mode/css/scss_test.js b/applications/admin/static/codemirror/mode/css/scss_test.js index bc2a0785..c51cb42b 100644 --- a/applications/admin/static/codemirror/mode/css/scss_test.js +++ b/applications/admin/static/codemirror/mode/css/scss_test.js @@ -1,34 +1,33 @@ (function() { - var mode = CodeMirror.getMode({tabSize: 1}, "text/x-scss"); + var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } - function IT(name) { test.indentation(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } MT('url_with_quotation', - "[tag foo] { [property background][operator :][string-2 url]([string test.jpg]) }"); + "[tag foo] { [property background]:[atom url]([string test.jpg]) }"); MT('url_with_double_quotes', - "[tag foo] { [property background][operator :][string-2 url]([string \"test.jpg\"]) }"); + "[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }"); MT('url_with_single_quotes', - "[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) }"); + "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }"); MT('string', "[def @import] [string \"compass/css3\"]"); MT('important_keyword', - "[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) [keyword !important] }"); + "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }"); MT('variable', - "[variable-2 $blue][operator :][atom #333]"); + "[variable-2 $blue]:[atom #333]"); MT('variable_as_attribute', - "[tag foo] { [property color][operator :][variable-2 $blue] }"); + "[tag foo] { [property color]:[variable-2 $blue] }"); MT('numbers', - "[tag foo] { [property padding][operator :][number 10px] [number 10] [number 10em] [number 8in] }"); + "[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }"); MT('number_percentage', - "[tag foo] { [property width][operator :][number 80%] }"); + "[tag foo] { [property width]:[number 80%] }"); MT('selector', "[builtin #hello][qualifier .world]{}"); @@ -40,45 +39,69 @@ "[comment /*foobar*/]"); MT('attribute_with_hyphen', - "[tag foo] { [property font-size][operator :][number 10px] }"); + "[tag foo] { [property font-size]:[number 10px] }"); MT('string_after_attribute', - "[tag foo] { [property content][operator :][string \"::\"] }"); + "[tag foo] { [property content]:[string \"::\"] }"); MT('directives', "[def @include] [qualifier .mixin]"); MT('basic_structure', - "[tag p] { [property background][operator :][keyword red]; }"); + "[tag p] { [property background]:[keyword red]; }"); MT('nested_structure', - "[tag p] { [tag a] { [property color][operator :][keyword red]; } }"); + "[tag p] { [tag a] { [property color]:[keyword red]; } }"); MT('mixin', "[def @mixin] [tag table-base] {}"); MT('number_without_semicolon', - "[tag p] {[property width][operator :][number 12]}", - "[tag a] {[property color][operator :][keyword red];}"); + "[tag p] {[property width]:[number 12]}", + "[tag a] {[property color]:[keyword red];}"); MT('atom_in_nested_block', - "[tag p] { [tag a] { [property color][operator :][atom #000]; } }"); + "[tag p] { [tag a] { [property color]:[atom #000]; } }"); MT('interpolation_in_property', - "[tag foo] { [operator #{][variable-2 $hello][operator }:][number 2]; }"); + "[tag foo] { #{[variable-2 $hello]}:[number 2]; }"); MT('interpolation_in_selector', - "[tag foo][operator #{][variable-2 $hello][operator }] { [property color][operator :][atom #000]; }"); + "[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }"); MT('interpolation_error', - "[tag foo][operator #{][error foo][operator }] { [property color][operator :][atom #000]; }"); + "[tag foo]#{[error foo]} { [property color]:[atom #000]; }"); MT("divide_operator", - "[tag foo] { [property width][operator :][number 4] [operator /] [number 2] }"); + "[tag foo] { [property width]:[number 4] [operator /] [number 2] }"); MT('nested_structure_with_id_selector', - "[tag p] { [builtin #hello] { [property color][operator :][keyword red]; } }"); + "[tag p] { [builtin #hello] { [property color]:[keyword red]; } }"); - IT('mixin', - "@mixin container[1 (][2 $a: 10][1 , ][2 $b: 10][1 , ][2 $c: 10]) [1 {]}"); + 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 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/applications/admin/static/codemirror/mode/css/test.js b/applications/admin/static/codemirror/mode/css/test.js index 43647833..b3f47767 100644 --- a/applications/admin/static/codemirror/mode/css/test.js +++ b/applications/admin/static/codemirror/mode/css/test.js @@ -1,68 +1,19 @@ (function() { - var mode = CodeMirror.getMode({tabSize: 1}, "css"); + var mode = CodeMirror.getMode({indentUnit: 2}, "css"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } - function IT(name) { test.indentation(name, mode, Array.prototype.slice.call(arguments, 1)); } - - // Requires at least one media query - MT("atMediaEmpty", - "[def @media] [error {] }"); - - MT("atMediaMultiple", - "[def @media] [keyword not] [attribute screen] [operator and] ([property color]), [keyword not] [attribute print] [operator and] ([property color]) { }"); - - MT("atMediaCheckStack", - "[def @media] [attribute screen] { } [tag foo] { }"); - - MT("atMediaCheckStack", - "[def @media] [attribute screen] ([property color]) { } [tag foo] { }"); - - MT("atMediaPropertyOnly", - "[def @media] ([property color]) { } [tag foo] { }"); - - MT("atMediaCheckStackInvalidAttribute", - "[def @media] [attribute&error foobarhello] { [tag foo] { } }"); - - MT("atMediaCheckStackInvalidAttribute", - "[def @media] [attribute&error foobarhello] { } [tag foo] { }"); - - // Error, because "and" is only allowed immediately preceding a media expression - MT("atMediaInvalidAttribute", - "[def @media] [attribute&error foobarhello] { }"); - - // Error, because "and" is only allowed immediately preceding a media expression - MT("atMediaInvalidAnd", - "[def @media] [error and] [attribute screen] { }"); - - // Error, because "not" is only allowed as the first item in each media query - MT("atMediaInvalidNot", - "[def @media] [attribute screen] [error not] ([error not]) { }"); - - // Error, because "only" is only allowed as the first item in each media query - MT("atMediaInvalidOnly", - "[def @media] [attribute screen] [error only] ([error only]) { }"); // Error, because "foobarhello" is neither a known type or property, but // property was expected (after "and"), and it should be in parenthese. MT("atMediaUnknownType", - "[def @media] [attribute screen] [operator and] [error foobarhello] { }"); - - // Error, because "color" is not a known type, but is a known property, and - // should be in parentheses. - MT("atMediaInvalidType", - "[def @media] [attribute screen] [operator and] [error color] { }"); - - // Error, because "print" is not a known property, but is a known type, - // and should not be in parenthese. - MT("atMediaInvalidProperty", - "[def @media] [attribute screen] [operator and] ([error print]) { }"); + "[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] [operator and] ([property&error foobarhello]) { }"); + "[def @media] [attribute screen] [keyword and] ([error foobarhello]) { }"); // Make sure nesting works with media queries MT("atMediaMaxWidthNested", - "[def @media] [attribute screen] [operator and] ([property max-width][operator :] [number 25px]) { [tag foo] { } }"); + "[def @media] [attribute screen] [keyword and] ([property max-width]: [number 25px]) { [tag foo] { } }"); MT("tagSelector", "[tag foo] { }"); @@ -74,57 +25,95 @@ "[builtin #foo] { [error #foo] }"); MT("tagSelectorUnclosed", - "[tag foo] { [property margin][operator :] [number 0] } [tag bar] { }"); + "[tag foo] { [property margin]: [number 0] } [tag bar] { }"); MT("tagStringNoQuotes", - "[tag foo] { [property font-family][operator :] [variable-2 hello] [variable-2 world]; }"); + "[tag foo] { [property font-family]: [variable hello] [variable world]; }"); MT("tagStringDouble", - "[tag foo] { [property font-family][operator :] [string \"hello world\"]; }"); + "[tag foo] { [property font-family]: [string \"hello world\"]; }"); MT("tagStringSingle", - "[tag foo] { [property font-family][operator :] [string 'hello world']; }"); + "[tag foo] { [property font-family]: [string 'hello world']; }"); MT("tagColorKeyword", - "[tag foo] {" + - "[property color][operator :] [keyword black];" + - "[property color][operator :] [keyword navy];" + - "[property color][operator :] [keyword yellow];" + - "}"); + "[tag foo] {", + " [property color]: [keyword black];", + " [property color]: [keyword navy];", + " [property color]: [keyword yellow];", + "}"); MT("tagColorHex3", - "[tag foo] { [property background][operator :] [atom #fff]; }"); + "[tag foo] { [property background]: [atom #fff]; }"); MT("tagColorHex6", - "[tag foo] { [property background][operator :] [atom #ffffff]; }"); + "[tag foo] { [property background]: [atom #ffffff]; }"); MT("tagColorHex4", - "[tag foo] { [property background][operator :] [atom&error #ffff]; }"); + "[tag foo] { [property background]: [atom&error #ffff]; }"); MT("tagColorHexInvalid", - "[tag foo] { [property background][operator :] [atom&error #ffg]; }"); + "[tag foo] { [property background]: [atom&error #ffg]; }"); MT("tagNegativeNumber", - "[tag foo] { [property margin][operator :] [number -5px]; }"); + "[tag foo] { [property margin]: [number -5px]; }"); MT("tagPositiveNumber", - "[tag foo] { [property padding][operator :] [number 5px]; }"); + "[tag foo] { [property padding]: [number 5px]; }"); MT("tagVendor", - "[tag foo] { [meta -foo-][property box-sizing][operator :] [meta -foo-][string-2 border-box]; }"); + "[tag foo] { [meta -foo-][property box-sizing]: [meta -foo-][atom border-box]; }"); MT("tagBogusProperty", - "[tag foo] { [property&error barhelloworld][operator :] [number 0]; }"); + "[tag foo] { [property&error barhelloworld]: [number 0]; }"); MT("tagTwoProperties", - "[tag foo] { [property margin][operator :] [number 0]; [property padding][operator :] [number 0]; }"); + "[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }"); MT("tagTwoPropertiesURL", - "[tag foo] { [property background][operator :] [string-2 url]([string //example.com/foo.png]); [property padding][operator :] [number 0]; }"); + "[tag foo] { [property background]: [atom url]([string //example.com/foo.png]); [property padding]: [number 0]; }"); MT("commentSGML", "[comment ]"); - IT("tagSelector", - "strong, em [1 { background][2 : rgba][3 (255, 255, 0, .2][2 )][1 ;]}"); + MT("commentSGML2", + "[comment ] [tag div] {}"); + + MT("indent_tagSelector", + "[tag strong], [tag em] {", + " [property background]: [atom 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]: [atom 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]: [atom url]([string http://blah]),", + " [atom url]([string http://foo]);", + "}"); })(); diff --git a/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js b/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js index ff6dfd2f..c316cd34 100644 --- a/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js +++ b/applications/admin/static/codemirror/mode/htmlembedded/htmlembedded.js @@ -58,8 +58,6 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { }; }, - electricChars: "/{}:", - innerMode: function(state) { if (state.token == scriptingDispatch) return {state: state.scriptState, mode: scriptingMode}; else return {state: state.htmlState, mode: htmlMixedMode}; diff --git a/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js b/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js index ec0c21d2..e9eab3b9 100644 --- a/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js +++ b/applications/admin/static/codemirror/mode/htmlmixed/htmlmixed.js @@ -44,7 +44,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { if (close > -1) stream.backUp(cur.length - close); else if (m = cur.match(/<\/?$/)) { stream.backUp(cur.length); - if (!stream.match(pat, false)) stream.match(cur[0]); + if (!stream.match(pat, false)) stream.match(cur); } return style; } @@ -93,8 +93,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { return CodeMirror.Pass; }, - electricChars: "/{}:", - innerMode: function(state) { return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; } diff --git a/applications/admin/static/codemirror/mode/javascript/javascript.js b/applications/admin/static/codemirror/mode/javascript/javascript.js index e8ace32d..fbf574b4 100644 --- a/applications/admin/static/codemirror/mode/javascript/javascript.js +++ b/applications/admin/static/codemirror/mode/javascript/javascript.js @@ -15,13 +15,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var jsKeywords = { "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, - "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, + "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), "function": kw("function"), "catch": kw("catch"), "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, - "this": kw("this") + "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C }; // Extend the 'normal' keywords with the TypeScript language extensions @@ -30,7 +31,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var tsKeywords = { // object-like things "interface": kw("interface"), - "class": kw("class"), "extends": kw("extends"), "constructor": kw("constructor"), @@ -40,8 +40,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "protected": kw("protected"), "static": kw("static"), - "super": kw("super"), - // types "string": type, "number": type, "bool": type, "any": type }; @@ -56,19 +54,16 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var isOperatorChar = /[+\-*&%=<>!?|~^]/; - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); - } - - function nextUntilUnescaped(stream, end) { - var escaped = false, next; + function readRegexp(stream) { + var escaped = false, next, inSet = false; while ((next = stream.next()) != null) { - if (next == end && !escaped) - return false; + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } escaped = !escaped && next == "\\"; } - return escaped; } // Used as scratch variables to communicate multiple values without @@ -78,50 +73,51 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { type = tp; content = cont; return style; } - function jsTokenBase(stream, state) { + function tokenBase(stream, state) { var ch = stream.next(); - if (ch == '"' || ch == "'") - return chain(stream, state, jsTokenString(ch)); - else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { return ret("number", "number"); - else if (/[\[\]{}\(\),;\:\.]/.test(ch)) + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { return ret(ch); - else if (ch == "0" && stream.eat(/x/i)) { + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); - } - else if (/\d/.test(ch)) { + } else if (/\d/.test(ch)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); - } - else if (ch == "/") { + } else if (ch == "/") { if (stream.eat("*")) { - return chain(stream, state, jsTokenComment); - } - else if (stream.eat("/")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { stream.skipToEnd(); return ret("comment", "comment"); - } - else if (state.lastType == "operator" || state.lastType == "keyword c" || - /^[\[{}\(,;:]$/.test(state.lastType)) { - nextUntilUnescaped(stream, "/"); + } else if (state.lastType == "operator" || state.lastType == "keyword c" || + state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) { + readRegexp(stream); stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla return ret("regexp", "string-2"); - } - else { + } else { stream.eatWhile(isOperatorChar); - return ret("operator", null, stream.current()); + return ret("operator", "operator", stream.current()); } - } - else if (ch == "#") { + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#") { stream.skipToEnd(); return ret("error", "error"); - } - else if (isOperatorChar.test(ch)) { + } else if (isOperatorChar.test(ch)) { stream.eatWhile(isOperatorChar); - return ret("operator", null, stream.current()); - } - else { + return ret("operator", "operator", stream.current()); + } else { stream.eatWhile(/[\w\$_]/); var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; return (known && state.lastType != ".") ? ret(known.type, known.style, word) : @@ -129,19 +125,23 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } } - function jsTokenString(quote) { + function tokenString(quote) { return function(stream, state) { - if (!nextUntilUnescaped(stream, quote)) - state.tokenize = jsTokenBase; + var escaped = false, next; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; return ret("string", "string"); }; } - function jsTokenComment(stream, state) { + function tokenComment(stream, state) { var maybeEnd = false, ch; while (ch = stream.next()) { if (ch == "/" && maybeEnd) { - state.tokenize = jsTokenBase; + state.tokenize = tokenBase; break; } maybeEnd = (ch == "*"); @@ -149,6 +149,50 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return ret("comment", "comment"); } + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) break; + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (/[$\w]/.test(ch)) { + sawSomething = true; + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + // Parser var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true}; @@ -165,6 +209,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function inScope(state, varname) { for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } } function parseJS(state, style, type, content, stream) { @@ -211,7 +259,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { state.localVars = {name: varname, next: state.localVars}; } else { if (inList(state.globalVars)) return; - state.globalVars = {name: varname, next: state.globalVars}; + if (parserConfig.globalVars) + state.globalVars = {name: varname, next: state.globalVars}; } } @@ -253,16 +302,15 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { }; } - function statement(type) { - if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex); + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); if (type == "keyword b") return cont(pushlex("form"), statement, poplex); if (type == "{") return cont(pushlex("}"), block, poplex); if (type == ";") return cont(); if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse); if (type == "function") return cont(functiondef); - if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), - poplex, statement, poplex); + if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); if (type == "variable") return cont(pushlex("stat"), maybelabel); if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), block, poplex, poplex); @@ -270,6 +318,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "default") return cont(expect(":")); if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext); + if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex); + if (type == "class") return cont(pushlex("form"), className, objlit, poplex); + if (type == "export") return cont(pushlex("form"), afterExport, poplex); + if (type == "import") return cont(pushlex("form"), afterImport, poplex); return pass(pushlex("stat"), expression, expect(";"), poplex); } function expression(type) { @@ -279,14 +331,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return expressionInner(type, true); } function expressionInner(type, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (type == "function") return cont(functiondef); if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression); - if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); - if (type == "operator") return cont(noComma ? expressionNoComma : expression); - if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop); - if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop); + if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); return cont(); } function maybeexpression(type) { @@ -305,16 +363,39 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeoperatorNoComma(type, value, noComma) { var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; var expr = noComma == false ? expression : expressionNoComma; + if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value)) return cont(me); if (value == "?") return cont(expression, expect(":"), expr); return cont(expr); } + if (type == "quasi") { cx.cc.push(me); return quasi(value); } if (type == ";") return; - if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me); + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); if (type == ".") return cont(property, me); if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); } + function quasi(value) { + if (value.slice(value.length - 2) != "${") return cont(); + return cont(expression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + if (type == "{") return pass(statement); + return pass(expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + if (type == "{") return pass(statement); + return pass(expressionNoComma); + } function maybelabel(type) { if (type == ":") return cont(poplex, statement); return pass(maybeoperatorComma, expect(";"), poplex); @@ -328,15 +409,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "get" || value == "set") return cont(getterSetter); } else if (type == "number" || type == "string") { cx.marked = type + " property"; + } else if (type == "[") { + return cont(expression, expect("]"), afterprop); } - if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma); + if (atomicTypes.hasOwnProperty(type)) return cont(afterprop); } function getterSetter(type) { - if (type == ":") return cont(expression); - if (type != "variable") return cont(expect(":"), expression); + if (type != "variable") return pass(afterprop); cx.marked = "property"; return cont(functiondef); } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } function commasep(what, end) { function proceed(type) { if (type == ",") { @@ -349,75 +435,138 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } return function(type) { if (type == end) return cont(); - else return pass(what, proceed); + return pass(what, proceed); }; } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } function block(type) { if (type == "}") return cont(); return pass(statement, block); } function maybetype(type) { - if (type == ":") return cont(typedef); - return pass(); + if (isTS && type == ":") return cont(typedef); } function typedef(type) { if (type == "variable"){cx.marked = "variable-3"; return cont();} - return pass(); } - function vardef1(type, value) { - if (type == "variable") { + function vardef() { + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (type == "variable") { register(value); return cont(); } + if (type == "[") return contCommasep(pattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { register(value); - return isTS ? cont(maybetype, vardef2) : cont(vardef2); + return cont(maybeAssign); } - return pass(); + if (type == "variable") cx.marked = "property"; + return cont(expect(":"), pattern, maybeAssign); } - function vardef2(type, value) { - if (value == "=") return cont(expressionNoComma, vardef2); - if (type == ",") return cont(vardef1); + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); } function maybeelse(type, value) { if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex); } + function forspec(type) { + if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); + } function forspec1(type) { - if (type == "var") return cont(vardef1, expect(";"), forspec2); + if (type == "var") return cont(vardef, expect(";"), forspec2); if (type == ";") return cont(forspec2); - if (type == "variable") return cont(formaybein); + if (type == "variable") return cont(formaybeinof); return pass(expression, expect(";"), forspec2); } - function formaybein(_type, value) { - if (value == "in") return cont(expression); + function formaybeinof(_type, value) { + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } return cont(maybeoperatorComma, forspec2); } function forspec2(type, value) { if (type == ";") return cont(forspec3); - if (value == "in") return cont(expression); + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } return pass(expression, expect(";"), forspec3); } function forspec3(type) { if (type != ")") cont(expression); } function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);} - if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext); } - function funarg(type, value) { - if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} + function funarg(type) { + if (type == "spread") return cont(funarg); + return pass(pattern, maybetype); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(_type, value) { + if (value == "extends") return cont(expression); + } + function objlit(type) { + if (type == "{") return contCommasep(objprop, "}"); + } + function afterModule(type, value) { + if (type == "string") return cont(statement); + if (type == "variable") { register(value); return cont(maybeFrom); } + } + function afterExport(_type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + return pass(statement); + } + function afterImport(type) { + if (type == "string") return cont(); + return pass(importSpec, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + return cont(); + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(expressionNoComma, maybeArrayComprehension); + } + function maybeArrayComprehension(type) { + if (type == "for") return pass(comprehension, expect("]")); + if (type == ",") return cont(commasep(expressionNoComma, "]")); + return pass(commasep(expressionNoComma, "]")); + } + function comprehension(type) { + if (type == "for") return cont(forspec, comprehension); + if (type == "if") return cont(expression, comprehension); } // Interface return { startState: function(basecolumn) { - return { - tokenize: jsTokenBase, - lastType: null, + var state = { + tokenize: tokenBase, + lastType: "sof", cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, - globalVars: parserConfig.globalVars, context: parserConfig.localVars && {vars: parserConfig.localVars}, indented: 0 }; + if (parserConfig.globalVars) state.globalVars = parserConfig.globalVars; + return state; }, token: function(stream, state) { @@ -425,8 +574,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (!state.lexical.hasOwnProperty("align")) state.lexical.align = false; state.indented = stream.indentation(); + findFatArrow(stream, state); } - if (state.tokenize != jsTokenComment && stream.eatSpace()) return null; + if (state.tokenize != tokenComment && stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; @@ -434,21 +584,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { }, indent: function(state, textAfter) { - if (state.tokenize == jsTokenComment) return CodeMirror.Pass; - if (state.tokenize != jsTokenBase) return 0; + if (state.tokenize == tokenComment) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; // Kludge to prevent 'maybelse' from blocking lexical scope pops 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 || /^else\b/.test(textAfter)) break; + else if (c != maybeelse) break; } if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; - if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form") return lexical.indented + indentUnit; else if (type == "stat") diff --git a/applications/admin/static/codemirror/mode/meta.js b/applications/admin/static/codemirror/mode/meta.js index f6bbd1b4..f37cea55 100644 --- a/applications/admin/static/codemirror/mode/meta.js +++ b/applications/admin/static/codemirror/mode/meta.js @@ -18,6 +18,7 @@ CodeMirror.modeInfo = [ {name: 'Eiffel', mime: 'text/x-eiffel', mode: 'eiffel'}, {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'}, {name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'}, + {name: 'F#', mime: 'text/x-fsharp', mode: 'mllike'}, {name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, {name: 'Gherkin', mime: 'text/x-feature', mode: 'gherkin'}, {name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'}, @@ -36,7 +37,8 @@ CodeMirror.modeInfo = [ {name: 'JSON', mime: 'application/x-json', mode: 'javascript'}, {name: 'JSON', mime: 'application/json', mode: 'javascript'}, {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'}, - {name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'}, + {name: 'Jinja2', mime: null, mode: 'jinja2'}, + {name: 'Julia', mime: 'text/x-julia', mode: 'julia'}, {name: 'LESS', mime: 'text/x-less', mode: 'less'}, {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'}, {name: 'Lua', mime: 'text/x-lua', mode: 'lua'}, @@ -44,9 +46,10 @@ CodeMirror.modeInfo = [ {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'}, {name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'}, {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'}, - {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'}, + {name: 'OCaml', mime: 'text/x-ocaml', mode: 'mllike'}, {name: 'Octave', mime: 'text/x-octave', mode: 'octave'}, {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, + {name: 'PEG.js', mime: null, mode: 'pegjs'}, {name: 'Perl', mime: 'text/x-perl', mode: 'perl'}, {name: 'PHP', mime: 'text/x-php', mode: 'php'}, {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'}, diff --git a/applications/admin/static/codemirror/mode/python/python.js b/applications/admin/static/codemirror/mode/python/python.js index 802c2dd4..8bea5d19 100644 --- a/applications/admin/static/codemirror/mode/python/python.js +++ b/applications/admin/static/codemirror/mode/python/python.js @@ -11,6 +11,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); + var hangingIndent = parserConf.hangingIndent || parserConf.indentUnit; var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']); var commonkeywords = ['as', 'assert', 'break', 'class', 'continue', @@ -211,6 +212,11 @@ CodeMirror.defineMode("python", function(conf, parserConf) { break; } } + } else if (stream.match(/\s*($|#)/, false)) { + // An open paren/bracket/brace with only space or comments after it + // on the line will indent the next line a fixed amount, to make it + // easier to put arguments, list items, etc. on their own lines. + indentUnit = stream.indentation() + hangingIndent; } else { indentUnit = stream.column() + stream.current().length; } diff --git a/applications/admin/static/codemirror/mode/xml/xml.js b/applications/admin/static/codemirror/mode/xml/xml.js index 4f49e07f..96b51ff9 100644 --- a/applications/admin/static/codemirror/mode/xml/xml.js +++ b/applications/admin/static/codemirror/mode/xml/xml.js @@ -45,7 +45,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { var alignCDATA = parserConfig.alignCDATA; // Return variables for tokenizers - var tagName, type; + var tagName, type, setStyle; function inText(stream, state) { function chain(parser) { @@ -110,6 +110,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return null; } else if (ch == "<") { state.tokenize = inText; + state.state = baseState; + state.tagName = state.tagStart = null; var next = state.tokenize(stream, state); return next ? next + " error" : "error"; } else if (/[\'\"]/.test(ch)) { @@ -169,139 +171,124 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { }; } - var curState, curStream, setStyle; - function pass() { - for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); + function Context(state, tagName, startOfLine) { + this.prev = state.context; + this.tagName = tagName; + this.indent = state.indented; + this.startOfLine = startOfLine; + if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) + this.noIndent = true; } - function cont() { - pass.apply(null, arguments); - return true; + function popContext(state) { + if (state.context) state.context = state.context.prev; + } + function maybePopContext(state, nextTagName) { + var parentTagName; + while (true) { + if (!state.context) { + return; + } + parentTagName = state.context.tagName.toLowerCase(); + if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || + !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { + return; + } + popContext(state); + } } - function pushContext(tagName, startOfLine) { - var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); - curState.context = { - prev: curState.context, - tagName: tagName, - indent: curState.indented, - startOfLine: startOfLine, - noIndent: noIndent - }; - } - function popContext() { - if (curState.context) curState.context = curState.context.prev; - } - - function element(type) { + function baseState(type, stream, state) { if (type == "openTag") { - curState.tagName = tagName; - curState.tagStart = curStream.column(); - return cont(attributes, endtag(curState.startOfLine)); + state.tagName = tagName; + state.tagStart = stream.column(); + return attrState; } else if (type == "closeTag") { var err = false; - if (curState.context) { - if (curState.context.tagName != tagName) { - if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { - popContext(); - } - err = !curState.context || curState.context.tagName != tagName; + if (state.context) { + if (state.context.tagName != tagName) { + if (Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName.toLowerCase())) + popContext(state); + err = !state.context || state.context.tagName != tagName; } } else { err = true; } if (err) setStyle = "error"; - return cont(endclosetag(err)); + return err ? closeStateErr : closeState; + } else { + return baseState; } - return cont(); } - function endtag(startOfLine) { - return function(type) { - var tagName = curState.tagName; - curState.tagName = curState.tagStart = null; - if (type == "selfcloseTag" || - (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { - maybePopContext(tagName.toLowerCase()); - return cont(); - } - if (type == "endTag") { - maybePopContext(tagName.toLowerCase()); - pushContext(tagName, startOfLine); - return cont(); - } - return cont(); - }; - } - function endclosetag(err) { - return function(type) { - if (err) setStyle = "error"; - if (type == "endTag") { popContext(); return cont(); } + function closeState(type, _stream, state) { + if (type != "endTag") { setStyle = "error"; - return cont(arguments.callee); - }; - } - function maybePopContext(nextTagName) { - var parentTagName; - while (true) { - if (!curState.context) { - return; - } - parentTagName = curState.context.tagName.toLowerCase(); - if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || - !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { - return; - } - popContext(); + return closeState; } + popContext(state); + return baseState; + } + function closeStateErr(type, stream, state) { + setStyle = "error"; + return closeState(type, stream, state); } - function attributes(type) { - if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} - if (type == "endTag" || type == "selfcloseTag") return pass(); + function attrState(type, _stream, state) { + if (type == "word") { + setStyle = "attribute"; + return attrEqState; + } else if (type == "endTag" || type == "selfcloseTag") { + var tagName = state.tagName, tagStart = state.tagStart; + state.tagName = state.tagStart = null; + if (type == "selfcloseTag" || + Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase())) { + maybePopContext(state, tagName.toLowerCase()); + } else { + maybePopContext(state, tagName.toLowerCase()); + state.context = new Context(state, tagName, tagStart == state.indented); + } + return baseState; + } setStyle = "error"; - return cont(attributes); + return attrState; } - function attribute(type) { - if (type == "equals") return cont(attvalue, attributes); + function attrEqState(type, stream, state) { + if (type == "equals") return attrValueState; if (!Kludges.allowMissing) setStyle = "error"; - else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} - return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); + return attrState(type, stream, state); } - function attvalue(type) { - if (type == "string") return cont(attvaluemaybe); - if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} + function attrValueState(type, stream, state) { + if (type == "string") return attrContinuedState; + if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;} setStyle = "error"; - return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); + return attrState(type, stream, state); } - function attvaluemaybe(type) { - if (type == "string") return cont(attvaluemaybe); - else return pass(); + function attrContinuedState(type, stream, state) { + if (type == "string") return attrContinuedState; + return attrState(type, stream, state); } return { startState: function() { - return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null}; + return {tokenize: inText, + state: baseState, + indented: 0, + tagName: null, tagStart: null, + context: null}; }, token: function(stream, state) { - if (!state.tagName && stream.sol()) { - state.startOfLine = true; + if (!state.tagName && stream.sol()) state.indented = stream.indentation(); - } - if (stream.eatSpace()) return null; - setStyle = type = tagName = null; + if (stream.eatSpace()) return null; + tagName = type = null; var style = state.tokenize(stream, state); - state.type = type; if ((style || type) && style != "comment") { - curState = state; curStream = stream; - while (true) { - var comb = state.cc.pop() || element; - if (comb(type || style)) break; - } + setStyle = null; + state.state = state.state(type || style, stream, state); + if (setStyle) + style = setStyle == "error" ? style + " error" : setStyle; } - state.startOfLine = false; - if (setStyle) - style = setStyle == "error" ? style + " error" : setStyle; return style; }, @@ -311,8 +298,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { if (state.tokenize.isInAttribute) { return state.stringStartCol + 1; } - if ((state.tokenize != inTag && state.tokenize != inText) || - context && context.noIndent) + if (context && context.noIndent) return CodeMirror.Pass; + if (state.tokenize != inTag && state.tokenize != inText) return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; // Indent the starts of attribute names. if (state.tagName) { diff --git a/applications/admin/static/codemirror/package.json b/applications/admin/static/codemirror/package.json deleted file mode 100644 index 8310ab15..00000000 --- a/applications/admin/static/codemirror/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "codemirror", - "version":"3.19.0", - "main": "lib/codemirror.js", - "description": "In-browser code editing made bearable", - "licenses": [{"type": "MIT", - "url": "http://codemirror.net/LICENSE"}], - "directories": {"lib": "./lib"}, - "scripts": {"test": "node ./test/run.js"}, - "devDependencies": {"node-static": "0.6.0"}, - "bugs": "http://github.com/marijnh/CodeMirror/issues", - "keywords": ["JavaScript", "CodeMirror", "Editor"], - "homepage": "http://codemirror.net", - "maintainers":[{"name": "Marijn Haverbeke", - "email": "marijnh@gmail.com", - "web": "http://marijnhaverbeke.nl"}], - "repository": {"type": "git", - "url": "http://marijnhaverbeke.nl/git/codemirror"} -} diff --git a/applications/admin/static/codemirror/theme/ambiance.css b/applications/admin/static/codemirror/theme/ambiance.css index a69d16e5..3a54b2a0 100644 --- a/applications/admin/static/codemirror/theme/ambiance.css +++ b/applications/admin/static/codemirror/theme/ambiance.css @@ -33,7 +33,7 @@ .cm-s-ambiance .CodeMirror-selected { background: rgba(255, 255, 255, 0.15); } -.cm-s-ambiance .CodeMirror-focused .CodeMirror-selected { +.cm-s-ambiance.CodeMirror-focused .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } diff --git a/applications/admin/static/codemirror/theme/mbo.css b/applications/admin/static/codemirror/theme/mbo.css index f3250a73..bb52e6d1 100644 --- a/applications/admin/static/codemirror/theme/mbo.css +++ b/applications/admin/static/codemirror/theme/mbo.css @@ -24,11 +24,13 @@ .cm-s-mbo .CodeMirror-activeline-background {background: #494b41 !important;} .cm-s-mbo .CodeMirror-matchingbracket { - text-decoration: underline; + text-decoration: underline; color: #f5e107 !important; } + +.cm-s-mbo .CodeMirror-matchingtag {background: #4e4e4e;} -div.CodeMirror span.CodeMirror-searching { +.cm-s-mbo span.cm-searching { background-color: none; background: none; box-shadow: 0 0 0 1px #ffffec; diff --git a/applications/admin/static/codemirror/theme/midnight.css b/applications/admin/static/codemirror/theme/midnight.css index 66126322..468d87da 100644 --- a/applications/admin/static/codemirror/theme/midnight.css +++ b/applications/admin/static/codemirror/theme/midnight.css @@ -1,7 +1,7 @@ /* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ /**/ -.cm-s-midnight span.CodeMirror-matchhighlight { background: #494949 } +.cm-s-midnight span.CodeMirror-matchhighlight { background: #494949; } .cm-s-midnight.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67 !important; } /**/ diff --git a/applications/admin/static/codemirror/theme/pastel-on-dark.css b/applications/admin/static/codemirror/theme/pastel-on-dark.css new file mode 100644 index 00000000..df95699a --- /dev/null +++ b/applications/admin/static/codemirror/theme/pastel-on-dark.css @@ -0,0 +1,49 @@ +/** + * Pastel On Dark theme ported from ACE editor + * @license MIT + * @copyright AtomicPages LLC 2014 + * @author Dennis Thompson, AtomicPages LLC + * @version 1.1 + * @source https://github.com/atomicpages/codemirror-pastel-on-dark-theme + */ + +.cm-s-pastel-on-dark.CodeMirror { + background: #2c2827; + color: #8F938F; + line-height: 1.5; + font-family: consolas, Courier, monospace; + font-size: 14px; +} +.cm-s-pastel-on-dark div.CodeMirror-selected { background: rgba(221,240,255,0.2) !important; } +.cm-s-pastel-on-dark .CodeMirror-gutters { + background: #34302f; + border-right: 0px; + padding: 0 3px; +} +.cm-s-pastel-on-dark .CodeMirror-linenumber { color: #8F938F; } +.cm-s-pastel-on-dark .CodeMirror-cursor { border-left: 1px solid #A7A7A7 !important; } +.cm-s-pastel-on-dark span.cm-comment { color: #A6C6FF; } +.cm-s-pastel-on-dark span.cm-atom { color: #DE8E30; } +.cm-s-pastel-on-dark span.cm-number { color: #CCCCCC; } +.cm-s-pastel-on-dark span.cm-property { color: #8F938F; } +.cm-s-pastel-on-dark span.cm-attribute { color: #a6e22e; } +.cm-s-pastel-on-dark span.cm-keyword { color: #AEB2F8; } +.cm-s-pastel-on-dark span.cm-string { color: #66A968; } +.cm-s-pastel-on-dark span.cm-variable { color: #AEB2F8; } +.cm-s-pastel-on-dark span.cm-variable-2 { color: #BEBF55; } +.cm-s-pastel-on-dark span.cm-variable-3 { color: #DE8E30; } +.cm-s-pastel-on-dark span.cm-def { color: #757aD8; } +.cm-s-pastel-on-dark span.cm-bracket { color: #f8f8f2; } +.cm-s-pastel-on-dark span.cm-tag { color: #C1C144; } +.cm-s-pastel-on-dark span.cm-link { color: #ae81ff; } +.cm-s-pastel-on-dark span.cm-qualifier,.cm-s-pastel-on-dark span.cm-builtin { color: #C1C144; } +.cm-s-pastel-on-dark span.cm-error { + background: #757aD8; + color: #f8f8f0; +} +.cm-s-pastel-on-dark .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.031) !important; } +.cm-s-pastel-on-dark .CodeMirror-matchingbracket { + border: 1px solid rgba(255,255,255,0.25); + color: #8F938F !important; + margin: -1px -1px 0 -1px; +} diff --git a/applications/admin/static/codemirror/theme/the-matrix.css b/applications/admin/static/codemirror/theme/the-matrix.css index 9e60b7b0..0c3704a6 100644 --- a/applications/admin/static/codemirror/theme/the-matrix.css +++ b/applications/admin/static/codemirror/theme/the-matrix.css @@ -1,5 +1,5 @@ .cm-s-the-matrix.CodeMirror { background: #000000; color: #00FF00; } -.cm-s-the-matrix span.CodeMirror-selected { background: #a8f !important; } +.cm-s-the-matrix div.CodeMirror-selected { background: #2D2D2D !important; } .cm-s-the-matrix .CodeMirror-gutters { background: #060; border-right: 2px solid #00FF00; } .cm-s-the-matrix .CodeMirror-linenumber { color: #FFFFFF; } .cm-s-the-matrix .CodeMirror-cursor { border-left: 1px solid #00FF00 !important; }