diff --git a/applications/admin/languages/it.py b/applications/admin/languages/it.py index c97b6b39..d6776613 100644 --- a/applications/admin/languages/it.py +++ b/applications/admin/languages/it.py @@ -55,6 +55,7 @@ 'cannot create file': 'impossibile creare il file', 'cannot upload file "%(filename)s"': 'impossibile caricare il file "%(filename)s"', 'Change admin password': 'change admin password', +'change editor settings': 'change editor settings', 'change password': 'cambia password', 'check all': 'controlla tutto', 'Check for upgrades': 'check for upgrades', @@ -125,12 +126,14 @@ 'Editing italo': 'Editing italo', 'Editing italo3': 'Editing italo3', 'Editing Language file': 'Modifica file linguaggio', +'Editing welcome': 'Editing welcome', 'Enterprise Web Framework': 'Enterprise Web Framework', 'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"', 'Error snapshot': 'Error snapshot', 'Error ticket': 'Error ticket', 'Errors': 'errori', 'Exception instance attributes': 'Exception instance attributes', +'Exit Fullscreen': 'Exit Fullscreen', 'Expand Abbreviation': 'Expand Abbreviation', 'export as csv file': 'esporta come file CSV', 'exposes': 'espone', diff --git a/applications/admin/static/codemirror/CONTRIBUTING.md b/applications/admin/static/codemirror/CONTRIBUTING.md index 4e040ff1..8938f620 100644 --- a/applications/admin/static/codemirror/CONTRIBUTING.md +++ b/applications/admin/static/codemirror/CONTRIBUTING.md @@ -4,12 +4,12 @@ - [Submitting bug reports](#submitting-bug-reports-) - [Contributing code](#contributing-code-) -## Getting help [^](#how-to-contribute) +## Getting help Community discussion, questions, and informal bug reporting is done on the [CodeMirror Google group](http://groups.google.com/group/codemirror). -## Submitting bug reports [^](#how-to-contribute) +## Submitting bug reports The preferred way to report bugs is to use the [GitHub issue tracker](http://github.com/marijnh/CodeMirror/issues). Before @@ -45,7 +45,7 @@ should be asked on the [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and include the resulting link in your bug report. -## Contributing code [^](#how-to-contribute) +## Contributing code - Make sure you have a [GitHub Account](https://github.com/signup/free) - Fork [CodeMirror](https://github.com/marijnh/CodeMirror/) diff --git a/applications/admin/static/codemirror/LICENSE b/applications/admin/static/codemirror/LICENSE index 482d55eb..442d11cd 100644 --- a/applications/admin/static/codemirror/LICENSE +++ b/applications/admin/static/codemirror/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2013 by Marijn Haverbeke +Copyright (C) 2013 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -17,7 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Please note that some subdirectories of the CodeMirror distribution -include their own LICENSE files, and are released under different -licences. diff --git a/applications/admin/static/codemirror/addon/comment/comment.js b/applications/admin/static/codemirror/addon/comment/comment.js index 3c767446..cd2123e1 100644 --- a/applications/admin/static/codemirror/addon/comment/comment.js +++ b/applications/admin/static/codemirror/addon/comment/comment.js @@ -17,7 +17,7 @@ CodeMirror.defineExtension("lineComment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode; + var self = this, mode = self.getModeAt(from); var commentString = options.lineComment || mode.lineComment; if (!commentString) { if (options.blockCommentStart || mode.blockCommentStart) { @@ -52,7 +52,7 @@ CodeMirror.defineExtension("blockComment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode; + var self = this, mode = self.getModeAt(from); var startString = options.blockCommentStart || mode.blockCommentStart; var endString = options.blockCommentEnd || mode.blockCommentEnd; if (!startString || !endString) { @@ -85,7 +85,7 @@ CodeMirror.defineExtension("uncomment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode; + var self = this, mode = self.getModeAt(from); var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end); // Try finding line comments diff --git a/applications/admin/static/codemirror/addon/display/fullscreen.css b/applications/admin/static/codemirror/addon/display/fullscreen.css new file mode 100644 index 00000000..437acd89 --- /dev/null +++ b/applications/admin/static/codemirror/addon/display/fullscreen.css @@ -0,0 +1,6 @@ +.CodeMirror-fullscreen { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + height: auto; + z-index: 9; +} diff --git a/applications/admin/static/codemirror/addon/display/fullscreen.js b/applications/admin/static/codemirror/addon/display/fullscreen.js new file mode 100644 index 00000000..3c31e97a --- /dev/null +++ b/applications/admin/static/codemirror/addon/display/fullscreen.js @@ -0,0 +1,30 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { + if (old == CodeMirror.Init) old = false; + if (!old == !val) return; + if (val) setFullscreen(cm); + else setNormal(cm); + }); + + function setFullscreen(cm) { + 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.className += " CodeMirror-fullscreen"; + document.documentElement.style.overflow = "hidden"; + cm.refresh(); + } + + function setNormal(cm) { + var wrap = cm.getWrapperElement(); + wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); + document.documentElement.style.overflow = ""; + var info = cm.state.fullScreenRestore; + wrap.style.width = info.width; wrap.style.height = info.height; + window.scrollTo(info.scrollLeft, info.scrollTop); + cm.refresh(); + } +})(); diff --git a/applications/admin/static/codemirror/addon/edit/closebrackets.js b/applications/admin/static/codemirror/addon/edit/closebrackets.js index 6fbf38ae..88718b77 100644 --- a/applications/admin/static/codemirror/addon/edit/closebrackets.js +++ b/applications/admin/static/codemirror/addon/edit/closebrackets.js @@ -54,7 +54,9 @@ if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); - var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch); + var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch > 0 ? line.charAt(cur.ch - 1) : ""; + if (left == right && CodeMirror.isWordChar(curChar)) + return CodeMirror.Pass; if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); else diff --git a/applications/admin/static/codemirror/addon/edit/closetag.js b/applications/admin/static/codemirror/addon/edit/closetag.js index 454dfea5..0bc3e8be 100644 --- a/applications/admin/static/codemirror/addon/edit/closetag.js +++ b/applications/admin/static/codemirror/addon/edit/closetag.js @@ -27,9 +27,9 @@ if (val && (old == CodeMirror.Init || !old)) { var map = {name: "autoCloseTags"}; if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); }; + map["'/'"] = function(cm) { return autoCloseSlash(cm); }; if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); }; + map["'>'"] = function(cm) { return autoCloseGT(cm); }; cm.addKeyMap(map); } else if (!val && (old != CodeMirror.Init && old)) { cm.removeKeyMap("autoCloseTags"); @@ -41,40 +41,41 @@ var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; - function autoCloseTag(cm, ch) { + 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") return CodeMirror.Pass; + if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); - if (ch == ">" && state.tagName) { - var tagName = state.tagName; - 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" || - tok.string.indexOf("/") == (tok.string.length - 1) || // match something like - dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) - return CodeMirror.Pass; + var tagName = state.tagName; + 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" || + tok.string.indexOf("/") == (tok.string.length - 1) || // match something like + dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) + return CodeMirror.Pass; - var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; - var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); - cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", - {head: curPos, anchor: curPos}); - if (doIndent) { - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); - } - return; - } else if (ch == "/" && tok.string == "<") { - var tagName = state.context && state.context.tagName; - if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); - return; + var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; + var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); + cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", + {head: curPos, anchor: curPos}); + if (doIndent) { + cm.indentLine(pos.line + 1); + cm.indentLine(pos.line + 2); } - return CodeMirror.Pass; + } + + 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) != "<" || inner.mode.name != "xml") return CodeMirror.Pass; + + var tagName = state.context && state.context.tagName; + if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); } function indexOf(collection, elt) { diff --git a/applications/admin/static/codemirror/addon/edit/continuecomment.js b/applications/admin/static/codemirror/addon/edit/continuecomment.js deleted file mode 100644 index 30802622..00000000 --- a/applications/admin/static/codemirror/addon/edit/continuecomment.js +++ /dev/null @@ -1,44 +0,0 @@ -(function() { - var modes = ["clike", "css", "javascript"]; - for (var i = 0; i < modes.length; ++i) - CodeMirror.extendMode(modes[i], {blockCommentStart: "/*", - blockCommentEnd: "*/", - blockCommentContinue: " * "}); - - function continueComment(cm) { - var pos = cm.getCursor(), token = cm.getTokenAt(pos); - var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; - var space; - - if (token.type == "comment" && mode.blockCommentStart) { - var end = token.string.indexOf(mode.blockCommentEnd); - var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; - if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { - // Comment ended, don't continue it - } else if (token.string.indexOf(mode.blockCommentStart) == 0) { - space = full.slice(0, token.start); - if (!/^\s*$/.test(space)) { - space = ""; - for (var i = 0; i < token.start; ++i) space += " "; - } - } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && - found + mode.blockCommentContinue.length > token.start && - /^\s*$/.test(full.slice(0, found))) { - space = full.slice(0, found); - } - } - - if (space != null) - cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end"); - else - return CodeMirror.Pass; - } - - CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { - if (prev && prev != CodeMirror.Init) - cm.removeKeyMap("continueComment"); - var map = {name: "continueComment"}; - map[typeof val == "string" ? val : "Enter"] = continueComment; - cm.addKeyMap(map); - }); -})(); diff --git a/applications/admin/static/codemirror/addon/fold/brace-fold.js b/applications/admin/static/codemirror/addon/fold/brace-fold.js index e35115b8..2560b2b9 100644 --- a/applications/admin/static/codemirror/addon/fold/brace-fold.js +++ b/applications/admin/static/codemirror/addon/fold/brace-fold.js @@ -1,10 +1,10 @@ -CodeMirror.braceRangeFinder = function(cm, start) { +CodeMirror.registerHelper("fold", "brace", function(cm, start) { var line = start.line, lineText = cm.getLine(line); var startCh, tokenType; function findOpening(openCh) { for (var at = start.ch, pass = 0;;) { - var found = lineText.lastIndexOf(openCh, at - 1); + var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); if (found == -1) { if (pass == 1) break; pass = 1; @@ -12,7 +12,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { continue; } if (pass == 1 && found < start.ch) break; - tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; + tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); if (!/^(comment|string)/.test(tokenType)) return found + 1; at = found - 1; } @@ -34,7 +34,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); if (pos == text.length) break; - if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { + if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { if (pos == nextOpen) ++count; else if (!--count) { end = i; endCh = pos; break outer; } } @@ -44,9 +44,10 @@ CodeMirror.braceRangeFinder = function(cm, start) { if (end == null || line == end && endCh == startCh) return; return {from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh)}; -}; +}); +CodeMirror.braceRangeFinder = CodeMirror.fold.brace; // deprecated -CodeMirror.importRangeFinder = function(cm, start) { +CodeMirror.registerHelper("fold", "import", function(cm, start) { function hasImport(line) { if (line < cm.firstLine() || line > cm.lastLine()) return null; var start = cm.getTokenAt(CodeMirror.Pos(line, 1)); @@ -68,9 +69,10 @@ CodeMirror.importRangeFinder = function(cm, start) { end = next.end; } return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end}; -}; +}); +CodeMirror.importRangeFinder = CodeMirror.fold["import"]; // deprecated -CodeMirror.includeRangeFinder = function(cm, start) { +CodeMirror.registerHelper("fold", "include", function(cm, start) { function hasInclude(line) { if (line < cm.firstLine() || line > cm.lastLine()) return null; var start = cm.getTokenAt(CodeMirror.Pos(line, 1)); @@ -87,4 +89,5 @@ CodeMirror.includeRangeFinder = function(cm, start) { } return {from: CodeMirror.Pos(start, has + 1), to: cm.clipPos(CodeMirror.Pos(end))}; -}; +}); +CodeMirror.includeRangeFinder = CodeMirror.fold.include; // deprecated diff --git a/applications/admin/static/codemirror/addon/fold/foldcode.js b/applications/admin/static/codemirror/addon/fold/foldcode.js index 2743d3e2..931c3726 100644 --- a/applications/admin/static/codemirror/addon/fold/foldcode.js +++ b/applications/admin/static/codemirror/addon/fold/foldcode.js @@ -2,7 +2,8 @@ "use strict"; function doFold(cm, pos, options) { - var finder = options.call ? options : (options && options.rangeFinder); + var finder = options && (options.call ? options : options.rangeFinder); + if (!finder) finder = cm.getHelper(pos, "fold"); if (!finder) return; if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); var minSize = options && options.minFoldSize || 0; @@ -29,12 +30,16 @@ if (!range || range.cleared) return; var myWidget = makeWidget(options); - CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();}); + CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); }); var myRange = cm.markText(range.from, range.to, { replacedWith: myWidget, clearOnEnter: true, __isFold: true }); + myRange.on("clear", function(from, to) { + CodeMirror.signal(cm, "unfold", cm, from, to); + }); + CodeMirror.signal(cm, "fold", cm, range.from, range.to); } function makeWidget(options) { @@ -56,7 +61,7 @@ // New-style interface CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); }); - CodeMirror.combineRangeFinders = function() { + CodeMirror.registerHelper("fold", "combine", function() { var funcs = Array.prototype.slice.call(arguments, 0); return function(cm, start) { for (var i = 0; i < funcs.length; ++i) { @@ -64,5 +69,5 @@ if (found) return found; } }; - }; + }); })(); diff --git a/applications/admin/static/codemirror/addon/fold/indent-fold.js b/applications/admin/static/codemirror/addon/fold/indent-fold.js index 94a0a1ff..fcbff966 100644 --- a/applications/admin/static/codemirror/addon/fold/indent-fold.js +++ b/applications/admin/static/codemirror/addon/fold/indent-fold.js @@ -1,4 +1,4 @@ -CodeMirror.indentRangeFinder = function(cm, start) { +CodeMirror.registerHelper("fold", "indent", function(cm, start) { var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { @@ -8,4 +8,5 @@ CodeMirror.indentRangeFinder = function(cm, start) { return {from: CodeMirror.Pos(start.line, firstLine.length), to: CodeMirror.Pos(i, curLine.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 b764bc01..88a107c4 100644 --- a/applications/admin/static/codemirror/addon/fold/xml-fold.js +++ b/applications/admin/static/codemirror/addon/fold/xml-fold.js @@ -2,14 +2,17 @@ "use strict"; var Pos = CodeMirror.Pos; + function cmp(a, b) { return a.line - b.line || a.ch - b.ch; } var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); - function Iter(cm, line, ch) { + function Iter(cm, line, ch, range) { this.line = line; this.ch = ch; this.cm = cm; this.text = cm.getLine(line); + this.min = range ? range.from : cm.firstLine(); + this.max = range ? range.to - 1 : cm.lastLine(); } function tagAt(iter, ch) { @@ -18,13 +21,13 @@ } function nextLine(iter) { - if (iter.line >= iter.cm.lastLine()) return; + if (iter.line >= iter.max) return; iter.ch = 0; iter.text = iter.cm.getLine(++iter.line); return true; } function prevLine(iter) { - if (iter.line <= iter.cm.firstLine()) return; + if (iter.line <= iter.min) return; iter.text = iter.cm.getLine(--iter.line); iter.ch = iter.text.length; return true; @@ -43,7 +46,7 @@ } function toTagStart(iter) { for (;;) { - var lt = iter.text.lastIndexOf("<", iter.ch - 1); + var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1; if (lt == -1) { if (prevLine(iter)) continue; else return; } if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; } xmlTagStart.lastIndex = lt; @@ -65,7 +68,7 @@ } function toPrevTag(iter) { for (;;) { - var gt = iter.text.lastIndexOf(">", iter.ch - 1); + var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1; if (gt == -1) { if (prevLine(iter)) continue; else return; } if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; } var lastSlash = iter.text.lastIndexOf("/", gt); @@ -121,7 +124,7 @@ } } - CodeMirror.tagRangeFinder = function(cm, start) { + CodeMirror.registerHelper("fold", "xml", function(cm, start) { var iter = new Iter(cm, start.line, 0); for (;;) { var openTag = toNextTag(iter), end; @@ -132,27 +135,31 @@ return close && {from: start, to: close.from}; } } - }; + }); + CodeMirror.tagRangeFinder = CodeMirror.fold.xml; // deprecated - CodeMirror.findMatchingTag = function(cm, pos) { - var iter = new Iter(cm, pos.line, pos.ch); - var end = toTagEnd(iter), start = toTagStart(iter); - if (!end || end == "selfClose" || !start) return; + CodeMirror.findMatchingTag = function(cm, pos, range) { + var iter = new Iter(cm, pos.line, pos.ch, range); + if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return; + var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); + var start = end && toTagStart(iter); + if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; + var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]}; if (start[1]) { // closing tag - return findMatchingOpen(iter, start[2]); + return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"}; } else { // opening tag - toTagEnd(iter); - return findMatchingClose(iter, start[2]); + iter = new Iter(cm, to.line, to.ch, range); + return {open: here, close: findMatchingClose(iter, start[2]), at: "open"}; } }; - CodeMirror.findEnclosingTag = function(cm, pos) { - var iter = new Iter(cm, pos.line, pos.ch); + CodeMirror.findEnclosingTag = function(cm, pos, range) { + var iter = new Iter(cm, pos.line, pos.ch, range); for (;;) { var open = findMatchingOpen(iter); if (!open) break; - var forward = new Iter(cm, pos.line, pos.ch); + var forward = new Iter(cm, pos.line, pos.ch, range); var close = findMatchingClose(forward, open.tag); if (close) return {open: open, close: close}; } diff --git a/applications/admin/static/codemirror/addon/hint/html-hint.js b/applications/admin/static/codemirror/addon/hint/html-hint.js index 23238df0..cf256851 100755 --- a/applications/admin/static/codemirror/addon/hint/html-hint.js +++ b/applications/admin/static/codemirror/addon/hint/html-hint.js @@ -327,9 +327,11 @@ populate(data[tag]); CodeMirror.htmlSchema = data; - CodeMirror.htmlHint = function(cm, options) { + function htmlHint(cm, options) { var local = {schemaInfo: data}; if (options) for (var opt in options) local[opt] = options[opt]; - return CodeMirror.xmlHint(cm, local); - }; + return CodeMirror.hint.xml(cm, local); + } + CodeMirror.htmlHint = htmlHint; // deprecated + CodeMirror.registerHelper("hint", "html", htmlHint); })(); diff --git a/applications/admin/static/codemirror/addon/hint/javascript-hint.js b/applications/admin/static/codemirror/addon/hint/javascript-hint.js index b961c5ab..042fe132 100644 --- a/applications/admin/static/codemirror/addon/hint/javascript-hint.js +++ b/applications/admin/static/codemirror/addon/hint/javascript-hint.js @@ -56,11 +56,13 @@ to: Pos(cur.line, token.end)}; } - CodeMirror.javascriptHint = function(editor, options) { + function javascriptHint(editor, options) { return scriptHint(editor, javascriptKeywords, function (e, cur) {return e.getTokenAt(cur);}, options); }; + CodeMirror.javascriptHint = javascriptHint; // deprecated + CodeMirror.registerHelper("hint", "javascript", javascriptHint); function getCoffeeScriptToken(editor, cur) { // This getToken, it is for coffeescript, imitates the behavior of @@ -80,9 +82,11 @@ return token; } - CodeMirror.coffeescriptHint = function(editor, options) { + function coffeescriptHint(editor, options) { return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); - }; + } + CodeMirror.coffeescriptHint = coffeescriptHint; // deprecated + CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint); var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + "toUpperCase toLowerCase split concat match replace search").split(" "); diff --git a/applications/admin/static/codemirror/addon/hint/pig-hint.js b/applications/admin/static/codemirror/addon/hint/pig-hint.js index d831ccfe..7ef336ce 100644 --- a/applications/admin/static/codemirror/addon/hint/pig-hint.js +++ b/applications/admin/static/codemirror/addon/hint/pig-hint.js @@ -1,4 +1,6 @@ (function () { + "use strict"; + function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } @@ -41,9 +43,11 @@ to: CodeMirror.Pos(cur.line, token.end)}; } - CodeMirror.pigHint = function(editor) { + function pigHint(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - }; + } + CodeMirror.pigHint = pigHint; // deprecated + CodeMirror.registerHelper("hint", "pig", pigHint); var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " diff --git a/applications/admin/static/codemirror/addon/hint/python-hint.js b/applications/admin/static/codemirror/addon/hint/python-hint.js index 60221b89..98d2a589 100644 --- a/applications/admin/static/codemirror/addon/hint/python-hint.js +++ b/applications/admin/static/codemirror/addon/hint/python-hint.js @@ -41,9 +41,11 @@ to: CodeMirror.Pos(cur.line, token.end)}; } - CodeMirror.pythonHint = function(editor) { + function pythonHint(editor) { return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - }; + } + CodeMirror.pythonHint = pythonHint; // deprecated + CodeMirror.registerHelper("hint", "python", pythonHint); var pythonKeywords = "and del from not while as elif global or with assert else if pass yield" + "break except import print class exec in raise continue finally is return def for lambda try"; diff --git a/applications/admin/static/codemirror/addon/hint/show-hint.js b/applications/admin/static/codemirror/addon/hint/show-hint.js index 35e5cbb7..dbf41552 100644 --- a/applications/admin/static/codemirror/addon/hint/show-hint.js +++ b/applications/admin/static/codemirror/addon/hint/show-hint.js @@ -4,6 +4,8 @@ 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 (cm.state.completionActive) cm.state.completionActive.close(); @@ -25,10 +27,10 @@ Completion.prototype = { close: function() { if (!this.active()) return; + this.cm.state.completionActive = null; if (this.widget) this.widget.close(); if (this.onClose) this.onClose(); - this.cm.state.completionActive = null; CodeMirror.signal(this.cm, "endCompletion", this.cm); }, @@ -65,24 +67,21 @@ finished = true; completion.close(); completion.cm.off("cursorActivity", activity); - CodeMirror.signal(data, "close"); - } - function isDone() { - if (finished) return true; - if (!completion.widget) { done(); return true; } + if (data) CodeMirror.signal(data, "close"); } function update() { - if (isDone()) return; + if (finished) return; + CodeMirror.signal(data, "update"); if (completion.options.async) completion.getHints(completion.cm, finishUpdate, completion.options); else finishUpdate(completion.getHints(completion.cm, completion.options)); } - function finishUpdate(data) { - if (isDone()) return; + function finishUpdate(data_) { + data = data_; + if (finished) return; if (!data || !data.list.length) return done(); - completion.widget.close(); completion.widget = new Widget(completion, data); } @@ -91,10 +90,12 @@ 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)))) + (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) { completion.close(); - else + } else { debounce = setTimeout(update, 170); + if (completion.widget) completion.widget.close(); + } } this.cm.on("cursorActivity", activity); this.onClose = done; @@ -110,10 +111,10 @@ var baseMap = { Up: function() {handle.moveFocus(-1);}, Down: function() {handle.moveFocus(1);}, - PageUp: function() {handle.moveFocus(-handle.menuSize());}, - PageDown: function() {handle.moveFocus(handle.menuSize());}, + PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, + PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, Home: function() {handle.setFocus(0);}, - End: function() {handle.setFocus(handle.length);}, + End: function() {handle.setFocus(handle.length - 1);}, Enter: handle.pick, Tab: handle.pick, Esc: handle.close @@ -166,6 +167,7 @@ // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); + (options.container || document.body).appendChild(hints); var box = hints.getBoundingClientRect(); var overlapX = box.right - winW, overlapY = box.bottom - winH; if (overlapX > 0) { @@ -186,10 +188,9 @@ } hints.style.top = (top = pos.bottom - overlapY) + "px"; } - (options.container || document.body).appendChild(hints); cm.addKeyMap(this.keyMap = buildKeyMap(options, { - moveFocus: function(n) { widget.changeActive(widget.selectedHint + n); }, + moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, setFocus: function(n) { widget.changeActive(n); }, menuSize: function() { return widget.screenAmount(); }, length: completions.length, @@ -249,8 +250,11 @@ this.completion.pick(this.data, this.selectedHint); }, - changeActive: function(i) { - i = Math.max(0, Math.min(i, this.data.list.length - 1)); + changeActive: function(i, avoidWrap) { + if (i >= this.data.list.length) + i = avoidWrap ? this.data.list.length - 1 : 0; + else if (i < 0) + 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", ""); diff --git a/applications/admin/static/codemirror/addon/hint/xml-hint.js b/applications/admin/static/codemirror/addon/hint/xml-hint.js index ea5b8d54..b6c1da2c 100644 --- a/applications/admin/static/codemirror/addon/hint/xml-hint.js +++ b/applications/admin/static/codemirror/addon/hint/xml-hint.js @@ -3,7 +3,7 @@ var Pos = CodeMirror.Pos; - CodeMirror.xmlHint = function(cm, options) { + function getHints(cm, options) { var tags = options && options.schemaInfo; var quote = (options && options.quoteChar) || '"'; if (!tags) return; @@ -61,5 +61,8 @@ from: replaceToken ? Pos(cur.line, token.start) : cur, to: replaceToken ? Pos(cur.line, token.end) : cur }; - }; + } + + CodeMirror.xmlHint = getHints; // deprecated + CodeMirror.registerHelper("hint", "xml", getHints); })(); diff --git a/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js b/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js index 9c30de51..7f55a294 100644 --- a/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js +++ b/applications/admin/static/codemirror/addon/lint/coffeescript-lint.js @@ -1,6 +1,8 @@ // Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js -CodeMirror.coffeeValidator = function(text) { +// declare global: coffeelint + +CodeMirror.registerHelper("lint", "coffeescript", function(text) { var found = []; var parseError = function(err) { var loc = err.lineNumber; @@ -21,4 +23,5 @@ CodeMirror.coffeeValidator = function(text) { message: e.message}); } return found; -}; +}); +CodeMirror.coffeeValidator = CodeMirror.lint.coffeescript; // deprecated diff --git a/applications/admin/static/codemirror/addon/lint/javascript-lint.js b/applications/admin/static/codemirror/addon/lint/javascript-lint.js index 9a815c82..7123ab7e 100644 --- a/applications/admin/static/codemirror/addon/lint/javascript-lint.js +++ b/applications/admin/static/codemirror/addon/lint/javascript-lint.js @@ -1,4 +1,6 @@ (function() { + "use strict"; + // declare global: JSHINT var bogus = [ "Dangerous comment" ]; @@ -9,18 +11,15 @@ "Unmatched ", " and instead saw", " is not defined", "Unclosed string", "Stopping, unable to continue" ]; - function validator(options, text) { + function validator(text, options) { JSHINT(text, options); var errors = JSHINT.data().errors, result = []; if (errors) parseErrors(errors, result); return result; } - CodeMirror.javascriptValidatorWithOptions = function(options) { - return function(text) { return validator(options, text); }; - }; - - CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null); + CodeMirror.registerHelper("lint", "javascript", validator); + CodeMirror.javascriptValidator = CodeMirror.lint.javascript; // deprecated function cleanup(error) { // All problems are warnings by default diff --git a/applications/admin/static/codemirror/addon/lint/json-lint.js b/applications/admin/static/codemirror/addon/lint/json-lint.js index 42b36abb..e10e11bc 100644 --- a/applications/admin/static/codemirror/addon/lint/json-lint.js +++ b/applications/admin/static/codemirror/addon/lint/json-lint.js @@ -1,6 +1,8 @@ // Depends on jsonlint.js from https://github.com/zaach/jsonlint -CodeMirror.jsonValidator = function(text) { +// declare global: jsonlint + +CodeMirror.registerHelper("lint", "json", function(text) { var found = []; jsonlint.parseError = function(str, hash) { var loc = hash.loc; @@ -11,4 +13,5 @@ CodeMirror.jsonValidator = function(text) { try { jsonlint.parse(text); } catch(e) {} return found; -}; +}); +CodeMirror.jsonValidator = CodeMirror.lint.json; // deprecated diff --git a/applications/admin/static/codemirror/addon/lint/lint.css b/applications/admin/static/codemirror/addon/lint/lint.css index eb15381f..e592b367 100644 --- a/applications/admin/static/codemirror/addon/lint/lint.css +++ b/applications/admin/static/codemirror/addon/lint/lint.css @@ -57,40 +57,16 @@ } .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); } .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); } .CodeMirror-lint-marker-multiple { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; } - -/* Styles for the overview ruler -.annotationOverview { - cursor: pointer; - border-radius: 2px; - left: 2px; - width: 8px; -} -.annotationOverview.error { - background-color: lightcoral; - border: 1px solid darkred; -} -.annotationOverview.warning { - background-color: Gold; - border: 1px solid black; -} - -.annotationHTML.overlay { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); - background-position: right bottom; - position: relative; - top: -16px; -} -*/ \ No newline at end of file diff --git a/applications/admin/static/codemirror/addon/lint/lint.js b/applications/admin/static/codemirror/addon/lint/lint.js index 2e7cea19..b502ee41 100644 --- a/applications/admin/static/codemirror/addon/lint/lint.js +++ b/applications/admin/static/codemirror/addon/lint/lint.js @@ -1,4 +1,5 @@ -CodeMirror.validate = (function() { +(function() { + "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; var SEVERITIES = /^(?:error|warning)$/; @@ -52,9 +53,11 @@ CodeMirror.validate = (function() { this.onMouseOver = function(e) { onMouseOver(cm, e); }; } - function parseOptions(options) { + function parseOptions(cm, options) { if (options instanceof Function) return {getAnnotations: options}; - else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); + if (!options || options === true) options = {}; + if (!options.getAnnotations) options.getAnnotations = cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); return options; } @@ -109,7 +112,7 @@ CodeMirror.validate = (function() { if (options.async) options.getAnnotations(cm, updateLinting, options); else - updateLinting(cm, options.getAnnotations(cm.getValue())); + updateLinting(cm, options.getAnnotations(cm.getValue(), options)); } function updateLinting(cm, annotationsNotSorted) { @@ -175,7 +178,7 @@ CodeMirror.validate = (function() { } } - CodeMirror.defineOption("lintWith", false, function(cm, val, old) { + function optionHandler(cm, val, old) { if (old && old != CodeMirror.Init) { clearMarks(cm); cm.off("change", onChange); @@ -186,12 +189,15 @@ CodeMirror.validate = (function() { if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; - var state = cm.state.lint = new LintState(cm, parseOptions(val), hasLintGutter); + var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); cm.on("change", onChange); if (state.options.tooltips != false) CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); startLinting(cm); } - }); + } + + CodeMirror.defineOption("lintWith", false, optionHandler); // deprecated + CodeMirror.defineOption("lint", false, optionHandler); // deprecated })(); diff --git a/applications/admin/static/codemirror/addon/merge/merge.css b/applications/admin/static/codemirror/addon/merge/merge.css index 1f20f88a..63237fc8 100644 --- a/applications/admin/static/codemirror/addon/merge/merge.css +++ b/applications/admin/static/codemirror/addon/merge/merge.css @@ -1,24 +1,34 @@ -.CodeMirror-diff { +.CodeMirror-merge { position: relative; border: 1px solid #ddd; + white-space: pre; } -.CodeMirror-diff, .CodeMirror-diff .CodeMirror { +.CodeMirror-merge, .CodeMirror-merge .CodeMirror { height: 350px; } -.CodeMirror-diff-2pane .CodeMirror-diff-pane { width: 47%; } -.CodeMirror-diff-2pane .CodeMirror-diff-gap { width: 6%; } -.CodeMirror-diff-3pane .CodeMirror-diff-pane { width: 31%; } -.CodeMirror-diff-3pane .CodeMirror-diff-gap { width: 3.5%; } +.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; } +.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; } +.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; } +.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; } -.CodeMirror-diff-pane { - float: left; +.CodeMirror-merge-pane { + display: inline-block; + white-space: normal; + vertical-align: top; +} +.CodeMirror-merge-pane-rightmost { + position: absolute; + right: 0px; + z-index: 1; } -.CodeMirror-diff-gap { - float: left; +.CodeMirror-merge-gap { + z-index: 2; + display: inline-block; height: 100%; + -moz-box-sizing: border-box; box-sizing: border-box; overflow: hidden; border-left: 1px solid #ddd; @@ -27,11 +37,11 @@ background: #f8f8f8; } -.CodeMirror-diff-scrolllock-wrap { +.CodeMirror-merge-scrolllock-wrap { position: absolute; bottom: 0; left: 50%; } -.CodeMirror-diff-scrolllock { +.CodeMirror-merge-scrolllock { position: relative; left: -50%; cursor: pointer; @@ -39,44 +49,44 @@ line-height: 1; } -.CodeMirror-diff-copybuttons-left, .CodeMirror-diff-copybuttons-right { +.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right { position: absolute; left: 0; top: 0; right: 0; bottom: 0; line-height: 1; } -.CodeMirror-diff-copy { +.CodeMirror-merge-copy { position: absolute; cursor: pointer; color: #44c; } -.CodeMirror-diff-copybuttons-left .CodeMirror-diff-copy { left: 2px; } -.CodeMirror-diff-copybuttons-right .CodeMirror-diff-copy { right: 2px; } +.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } +.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } -.CodeMirror-diff-r-inserted, .CodeMirror-diff-l-inserted { +.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==); background-position: bottom left; background-repeat: repeat-x; } -.CodeMirror-diff-r-deleted, .CodeMirror-diff-l-deleted { +.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==); background-position: bottom left; background-repeat: repeat-x; } -.CodeMirror-diff-r-chunk { background: #ffffe0; } -.CodeMirror-diff-r-chunk-start { border-top: 1px solid #ee8; } -.CodeMirror-diff-r-chunk-end { border-bottom: 1px solid #ee8; } -.CodeMirror-diff-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } +.CodeMirror-merge-r-chunk { background: #ffffe0; } +.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; } +.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; } +.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } -.CodeMirror-diff-l-chunk { background: #eef; } -.CodeMirror-diff-l-chunk-start { border-top: 1px solid #88e; } -.CodeMirror-diff-l-chunk-end { border-bottom: 1px solid #88e; } -.CodeMirror-diff-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } +.CodeMirror-merge-l-chunk { background: #eef; } +.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; } +.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; } +.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } -.CodeMirror-diff-l-chunk.CodeMirror-diff-r-chunk { background: #dfd; } -.CodeMirror-diff-l-chunk-start.CodeMirror-diff-r-chunk-start { border-top: 1px solid #4e4; } -.CodeMirror-diff-l-chunk-end.CodeMirror-diff-r-chunk-end { border-bottom: 1px solid #4e4; } +.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; } +.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; } +.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; } diff --git a/applications/admin/static/codemirror/addon/merge/merge.js b/applications/admin/static/codemirror/addon/merge/merge.js index b1125185..70341e4d 100644 --- a/applications/admin/static/codemirror/addon/merge/merge.js +++ b/applications/admin/static/codemirror/addon/merge/merge.js @@ -1,5 +1,6 @@ (function() { "use strict"; + // declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL var Pos = CodeMirror.Pos; var svgNS = "http://www.w3.org/2000/svg"; @@ -8,18 +9,18 @@ this.mv = mv; this.type = type; this.classes = type == "left" - ? {chunk: "CodeMirror-diff-l-chunk", - start: "CodeMirror-diff-l-chunk-start", - end: "CodeMirror-diff-l-chunk-end", - insert: "CodeMirror-diff-l-inserted", - del: "CodeMirror-diff-l-deleted", - connect: "CodeMirror-diff-l-connect"} - : {chunk: "CodeMirror-diff-r-chunk", - start: "CodeMirror-diff-r-chunk-start", - end: "CodeMirror-diff-r-chunk-end", - insert: "CodeMirror-diff-r-inserted", - del: "CodeMirror-diff-r-deleted", - connect: "CodeMirror-diff-r-connect"}; + ? {chunk: "CodeMirror-merge-l-chunk", + start: "CodeMirror-merge-l-chunk-start", + end: "CodeMirror-merge-l-chunk-end", + insert: "CodeMirror-merge-l-inserted", + del: "CodeMirror-merge-l-deleted", + connect: "CodeMirror-merge-l-connect"} + : {chunk: "CodeMirror-merge-r-chunk", + start: "CodeMirror-merge-r-chunk-start", + end: "CodeMirror-merge-r-chunk-end", + insert: "CodeMirror-merge-r-inserted", + del: "CodeMirror-merge-r-deleted", + connect: "CodeMirror-merge-r-connect"}; } DiffView.prototype = { @@ -31,9 +32,17 @@ this.diff = getDiff(orig, options.value); this.diffOutOfDate = false; + this.showDifferences = options.showDifferences !== false; this.forceUpdate = registerUpdate(this); setScrollLock(this, true, false); registerScroll(this); + }, + setShowDifferences: function(val) { + val = val !== false; + if (val != this.showDifferences) { + this.showDifferences = val; + this.forceUpdate("full"); + } } }; @@ -41,26 +50,38 @@ var edit = {from: 0, to: 0, marked: []}; var orig = {from: 0, to: 0, marked: []}; var debounceChange; - function update() { + function update(mode) { + if (mode == "full") { + if (dv.svg) clear(dv.svg); + clear(dv.copyButtons); + clearMarks(dv.edit, edit.marked, dv.classes); + clearMarks(dv.orig, orig.marked, dv.classes); + edit.from = edit.to = orig.from = orig.to = 0; + } if (dv.diffOutOfDate) { dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue()); dv.diffOutOfDate = false; + CodeMirror.signal(dv.edit, "updateDiff", dv.diff); + } + if (dv.showDifferences) { + updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes); + updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes); } - updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes); - updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes); drawConnectors(dv); } function set(slow) { clearTimeout(debounceChange); debounceChange = setTimeout(update, slow == true ? 250 : 100); } - dv.edit.on("change", function() { + function change() { if (!dv.diffOutOfDate) { dv.diffOutOfDate = true; edit.from = edit.to = orig.from = orig.to = 0; } set(true); - }); + } + dv.edit.on("change", change); + dv.orig.on("change", change); dv.edit.on("viewportChange", set); dv.orig.on("viewportChange", set); update(); @@ -93,7 +114,21 @@ var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig); var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit); var ratio = (midY - off.top) / (off.bot - off.top); - other.scrollTo(null, (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top)); + var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top); + + var botDist, mix; + // Some careful tweaking to make sure no space is left out of view + // when scrolling to top or bottom. + if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) { + targetPos = targetPos * mix + sInfo.top * (1 - mix); + } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) { + var otherInfo = other.getScrollInfo(); + var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos; + if (botDistOther > botDist && (mix = botDist / halfScreen) < 1) + targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix); + } + + other.scrollTo(sInfo.left, targetPos); other.state.scrollSetAt = now; other.state.scrollSetBy = dv; return true; @@ -197,6 +232,8 @@ // Updating the gap between editor and original function drawConnectors(dv) { + if (!dv.showDifferences) return; + if (dv.svg) { clear(dv.svg); var w = dv.gap.offsetWidth; @@ -208,8 +245,8 @@ var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport(); var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top; iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) { - if (topEdit >= vpEdit.to || botEdit < vpEdit.from || - topOrig >= vpOrig.to || botOrig < vpOrig.from) + if (topEdit > vpEdit.to || botEdit < vpEdit.from || + topOrig > vpOrig.to || botOrig < vpOrig.from) return; var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx; if (dv.svg) { @@ -225,7 +262,7 @@ "class", dv.classes.connect); } var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", - "CodeMirror-diff-copy")); + "CodeMirror-merge-copy")); copy.title = "Revert chunk"; copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig}; copy.style.top = top + "px"; @@ -250,23 +287,25 @@ if (hasLeft) { left = this.left = new DiffView(this, "left"); - var leftPane = elt("div", null, "CodeMirror-diff-pane"); + var leftPane = elt("div", null, "CodeMirror-merge-pane"); wrap.push(leftPane); wrap.push(buildGap(left)); } - var editPane = elt("div", null, "CodeMirror-diff-pane"); + var editPane = elt("div", null, "CodeMirror-merge-pane"); wrap.push(editPane); if (hasRight) { right = this.right = new DiffView(this, "right"); wrap.push(buildGap(right)); - var rightPane = elt("div", null, "CodeMirror-diff-pane"); + var rightPane = elt("div", null, "CodeMirror-merge-pane"); wrap.push(rightPane); } + (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; + wrap.push(elt("div", null, null, "height: 0; clear: both;")); - var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-diff CodeMirror-diff-" + panes + "pane")); + var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); this.edit = CodeMirror(editPane, copyObj(options)); if (left) left.init(leftPane, origLeft, options); @@ -284,11 +323,11 @@ }; function buildGap(dv) { - var lock = dv.lockButton = elt("div", null, "CodeMirror-diff-scrolllock"); + var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock"); lock.title = "Toggle locked scrolling"; - var lockWrap = elt("div", [lock], "CodeMirror-diff-scrolllock-wrap"); + var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); - dv.copyButtons = elt("div", null, "CodeMirror-diff-copybuttons-" + dv.type); + dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); CodeMirror.on(dv.copyButtons, "click", function(e) { var node = e.target || e.srcElement; if (node.chunk) copyChunk(dv, node.chunk); @@ -299,14 +338,18 @@ dv.svg = svg; if (svg) gapElts.push(svg); - return dv.gap = elt("div", gapElts, "CodeMirror-diff-gap"); + return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap"); } MergeView.prototype = { constuctor: MergeView, editor: function() { return this.edit; }, rightOriginal: function() { return this.right && this.right.orig; }, - leftOriginal: function() { return this.left && this.left.orig; } + leftOriginal: function() { return this.left && this.left.orig; }, + setShowDifferences: function(val) { + if (this.right) this.right.setShowDifferences(val); + if (this.left) this.left.setShowDifferences(val); + } }; // Operations on diffs diff --git a/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js b/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js index 7a9b82ff..d117166c 100644 --- a/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js +++ b/applications/admin/static/codemirror/addon/runmode/runmode-standalone.js @@ -2,6 +2,9 @@ window.CodeMirror = {}; +(function() { +"use strict"; + function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { @@ -43,12 +46,14 @@ StringStream.prototype = { match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { if (consume !== false) this.pos += pattern.length; return true; } } else { var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; if (match && consume !== false) this.pos += match[0].length; return match; } @@ -123,8 +128,9 @@ CodeMirror.runMode = function (string, modespec, callback, options) { var stream = new CodeMirror.StringStream(lines[i]); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start); + callback(stream.current(), style, i, stream.start, state); stream.start = stream.pos; } } }; +})(); diff --git a/applications/admin/static/codemirror/addon/runmode/runmode.js b/applications/admin/static/codemirror/addon/runmode/runmode.js index a7da6d71..7aafa2ad 100644 --- a/applications/admin/static/codemirror/addon/runmode/runmode.js +++ b/applications/admin/static/codemirror/addon/runmode/runmode.js @@ -49,7 +49,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) { var stream = new CodeMirror.StringStream(lines[i]); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start); + callback(stream.current(), style, i, stream.start, state); stream.start = stream.pos; } } diff --git a/applications/admin/static/codemirror/addon/runmode/runmode.node.js b/applications/admin/static/codemirror/addon/runmode/runmode.node.js index a6ea919d..0f1088fa 100644 --- a/applications/admin/static/codemirror/addon/runmode/runmode.node.js +++ b/applications/admin/static/codemirror/addon/runmode/runmode.node.js @@ -41,12 +41,14 @@ StringStream.prototype = { match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { if (consume !== false) this.pos += pattern.length; return true; } } else { var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; if (match && consume !== false) this.pos += match[0].length; return match; } @@ -94,7 +96,7 @@ exports.runMode = function(string, modespec, callback) { var stream = new exports.StringStream(lines[i]); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start); + callback(stream.current(), style, i, stream.start, state); stream.start = stream.pos; } } diff --git a/applications/admin/static/codemirror/addon/search/match-highlighter.js b/applications/admin/static/codemirror/addon/search/match-highlighter.js index 21216758..e5cbeaca 100644 --- a/applications/admin/static/codemirror/addon/search/match-highlighter.js +++ b/applications/admin/static/codemirror/addon/search/match-highlighter.js @@ -15,15 +15,18 @@ (function() { var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; + var DEFAULT_DELAY = 100; function State(options) { if (typeof options == "object") { this.minChars = options.minChars; this.style = options.style; this.showToken = options.showToken; + this.delay = options.delay; } if (this.style == null) this.style = DEFAULT_TOKEN_STYLE; if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS; + if (this.delay == null) this.delay = DEFAULT_DELAY; this.overlay = this.timeout = null; } @@ -45,7 +48,7 @@ function cursorActivity(cm) { var state = cm.state.matchHighlighter; clearTimeout(state.timeout); - state.timeout = setTimeout(function() {highlightMatches(cm);}, 100); + state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay); } function highlightMatches(cm) { @@ -55,11 +58,13 @@ cm.removeOverlay(state.overlay); state.overlay = null; } - if (!cm.somethingSelected() && state.showToken) { - var tok = cm.getTokenAt(cm.getCursor()).string; - if (/\w/.test(tok)) - cm.addOverlay(state.overlay = makeOverlay(tok, true, state.style)); + var re = state.showToken === true ? /[\w$]/ : state.showToken; + var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; + while (start && re.test(line.charAt(start - 1))) --start; + while (end < line.length && re.test(line.charAt(end))) ++end; + if (start < end) + cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style)); return; } if (cm.getCursor("head").line != cm.getCursor("anchor").line) return; @@ -69,15 +74,15 @@ }); } - function boundariesAround(stream) { - return (stream.start || /.\b./.test(stream.string.slice(stream.start - 1, stream.start + 1))) && - (stream.pos == stream.string.length || /.\b./.test(stream.string.slice(stream.pos - 1, stream.pos + 1))); + function boundariesAround(stream, re) { + return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && + (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); } - function makeOverlay(query, wordBoundaries, style) { + function makeOverlay(query, hasBoundary, style) { return {token: function(stream) { if (stream.match(query) && - (!wordBoundaries || boundariesAround(stream))) + (!hasBoundary || boundariesAround(stream, hasBoundary))) return style; stream.next(); stream.skipTo(query.charAt(0)) || stream.skipToEnd(); diff --git a/applications/admin/static/codemirror/addon/search/searchcursor.js b/applications/admin/static/codemirror/addon/search/searchcursor.js index 3da3f04e..c034d586 100644 --- a/applications/admin/static/codemirror/addon/search/searchcursor.js +++ b/applications/admin/static/codemirror/addon/search/searchcursor.js @@ -69,8 +69,8 @@ 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) + 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; diff --git a/applications/admin/static/codemirror/addon/selection/active-line.js b/applications/admin/static/codemirror/addon/selection/active-line.js index 65fab6f1..e5050865 100644 --- a/applications/admin/static/codemirror/addon/selection/active-line.js +++ b/applications/admin/static/codemirror/addon/selection/active-line.js @@ -29,7 +29,7 @@ } function updateActiveLine(cm) { - var line = cm.getLineHandle(cm.getCursor().line); + var line = cm.getLineHandleVisualStart(cm.getCursor().line); if (cm.state.activeLine == line) return; clearActiveLine(cm); cm.addLineClass(line, "wrap", WRAP_CLASS); diff --git a/applications/admin/static/codemirror/bin/lint b/applications/admin/static/codemirror/bin/lint index bbb85b6c..4f70994c 100755 --- a/applications/admin/static/codemirror/bin/lint +++ b/applications/admin/static/codemirror/bin/lint @@ -1,11 +1,12 @@ #!/usr/bin/env node -var lint = require("../test/lint/lint"); +var lint = require("../test/lint/lint"), + path = require("path"); if (process.argv.length > 2) { lint.checkDir(process.argv[2]); } else { - process.chdir(__dirname.slice(0, __dirname.lastIndexOf("/"))); + process.chdir(path.resolve(__dirname, "..")); lint.checkDir("lib"); lint.checkDir("mode"); lint.checkDir("addon"); diff --git a/applications/admin/static/codemirror/keymap/emacs.js b/applications/admin/static/codemirror/keymap/emacs.js index 8727121c..7a3dfb11 100644 --- a/applications/admin/static/codemirror/keymap/emacs.js +++ b/applications/admin/static/codemirror/keymap/emacs.js @@ -353,7 +353,7 @@ }, "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": repeated("undo"), "K": "close", - "Delete": function(cm) { kill(cm, cm.getCursor(), sentenceEnd(cm, 1), true); }, + "Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); }, auto: "emacs", nofallthrough: true, disableInput: true }; diff --git a/applications/admin/static/codemirror/keymap/vim.js b/applications/admin/static/codemirror/keymap/vim.js index e05be031..9ad17af3 100644 --- a/applications/admin/static/codemirror/keymap/vim.js +++ b/applications/admin/static/codemirror/keymap/vim.js @@ -40,6 +40,10 @@ * TODO: Implement the remaining special marks. They have more complex * behavior. * + * Events: + * 'vim-mode-change' - raised on the editor anytime the current mode changes, + * Event object: {mode: "visual", subMode: "linewise"} + * * Code structure: * 1. Default keymap * 2. Variable declarations and short basic helpers @@ -206,8 +210,7 @@ // Operators { keys: ['d'], type: 'operator', operator: 'delete' }, { keys: ['y'], type: 'operator', operator: 'yank' }, - { keys: ['c'], type: 'operator', operator: 'change', - operatorArgs: { enterInsertMode: true } }, + { keys: ['c'], type: 'operator', operator: 'change' }, { keys: ['>'], type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }}, { keys: ['<'], type: 'operator', operator: 'indent', @@ -231,10 +234,11 @@ motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }}, { keys: ['C'], type: 'operatorMotion', - operator: 'change', operatorArgs: { enterInsertMode: true }, + operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }}, - { keys: ['~'], type: 'operatorMotion', operator: 'swapcase', + { keys: ['~'], type: 'operatorMotion', + operator: 'swapcase', operatorArgs: { shouldMoveCursor: true }, motion: 'moveByCharacters', motionArgs: { forward: true }}, // Actions { keys: [''], type: 'action', action: 'jumpListWalk', @@ -315,6 +319,28 @@ ]; var Vim = function() { + CodeMirror.defineOption('vimMode', false, function(cm, val) { + if (val) { + cm.setOption('keyMap', 'vim'); + CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); + cm.on('beforeSelectionChange', beforeSelectionChange); + maybeInitVimState(cm); + } else if (cm.state.vim) { + cm.setOption('keyMap', 'default'); + cm.off('beforeSelectionChange', beforeSelectionChange); + cm.state.vim = null; + } + }); + function beforeSelectionChange(cm, cur) { + var vim = cm.state.vim; + if (vim.insertMode || vim.exMode) return; + + var head = cur.head; + if (head.ch && head.ch == cm.doc.getLine(head.line).length) { + head.ch--; + } + } + var numberRegex = /[\d]/; var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)]; function makeKeyRange(start, size) { @@ -450,28 +476,11 @@ }; }; - // Global Vim state. Call getVimGlobalState to get and initialize. - var vimGlobalState; - function getVimGlobalState() { - if (!vimGlobalState) { - vimGlobalState = { - // The current search query. - searchQuery: null, - // Whether we are searching backwards. - searchIsReversed: false, - jumpList: createCircularJumpList(), - macroModeState: createMacroState(), - // Recording latest f, t, F or T motion command. - lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, - registerController: new RegisterController({}) - }; - } - return vimGlobalState; - } - function getVimState(cm) { - if (!cm.vimState) { + + function maybeInitVimState(cm) { + if (!cm.state.vim) { // Store instance state in the CodeMirror object. - cm.vimState = { + cm.state.vim = { inputState: new InputState(), // Vim's input state that triggered the last edit, used to repeat // motions and operators with '.'. @@ -500,7 +509,21 @@ visualLine: false }; } - return cm.vimState; + return cm.state.vim; + } + var vimGlobalState; + function resetVimGlobalState() { + vimGlobalState = { + // The current search query. + searchQuery: null, + // Whether we are searching backwards. + searchIsReversed: false, + jumpList: createCircularJumpList(), + macroModeState: createMacroState(), + // Recording latest f, t, F or T motion command. + lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, + registerController: new RegisterController({}) + }; } var vimApi= { @@ -510,16 +533,19 @@ // Testing hook, though it might be useful to expose the register // controller anyways. getRegisterController: function() { - return getVimGlobalState().registerController; + return vimGlobalState.registerController; }, // Testing hook. - clearVimGlobalState_: function() { - vimGlobalState = null; - }, + resetVimGlobalState_: resetVimGlobalState, + // Testing hook. getVimGlobalState_: function() { return vimGlobalState; }, + + // Testing hook. + maybeInitVimState_: maybeInitVimState, + InsertModeKey: InsertModeKey, map: function(lhs, rhs) { // Add user defined key bindings. @@ -532,17 +558,12 @@ exCommands[name]=func; exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'}; }, - // Initializes vim state variable on the CodeMirror object. Should only be - // called lazily by handleKey or for testing. - maybeInitState: function(cm) { - getVimState(cm); - }, // This is the outermost function called by CodeMirror, after keys have // been mapped to their Vim equivalents. handleKey: function(cm, key) { var command; - var vim = getVimState(cm); - var macroModeState = getVimGlobalState().macroModeState; + var vim = maybeInitVimState(cm); + var macroModeState = vimGlobalState.macroModeState; if (macroModeState.enteredMacroMode) { if (key == 'q') { actions.exitMacroRecordMode(); @@ -554,19 +575,17 @@ // Clear input state and get back to normal mode. vim.inputState = new InputState(); if (vim.visualMode) { - exitVisualMode(cm, vim); + exitVisualMode(cm); } return; } - if (vim.visualMode && - cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { - // The selection was cleared. Exit visual mode. - exitVisualMode(cm, vim); - } + // Enter visual mode when the mouse selects text. if (!vim.visualMode && !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { vim.visualMode = true; vim.visualLine = false; + CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"}); + cm.on('mousedown', exitVisualMode); } if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) { // Have to special case 0 since it's both a motion and a number. @@ -970,7 +989,7 @@ // cachedCursor is used to save the old position of the cursor // when * or # causes vim to seek for the nearest word and shift // the cursor before entering the motion. - getVimGlobalState().jumpList.cachedCursor = cm.getCursor(); + vimGlobalState.jumpList.cachedCursor = cm.getCursor(); cm.setCursor(word.start); handleQuery(query, true /** ignoreCase */, false /** smartCase */); @@ -1052,7 +1071,7 @@ return; } if (motionArgs.toJumplist) { - var jumpList = getVimGlobalState().jumpList; + var jumpList = vimGlobalState.jumpList; // if the current motion is # or *, use cachedCursor var cachedCursor = jumpList.cachedCursor; if (cachedCursor) { @@ -1091,6 +1110,11 @@ if (vim.visualLine) { if (cursorIsBefore(selectionStart, selectionEnd)) { selectionStart.ch = 0; + + var lastLine = cm.lastLine(); + if (selectionEnd.line > lastLine) { + selectionEnd.line = lastLine; + } selectionEnd.ch = lineLength(cm, selectionEnd.line); } else { selectionEnd.ch = 0; @@ -1146,15 +1170,12 @@ operators[operator](cm, operatorArgs, vim, curStart, curEnd, curOriginal); if (vim.visualMode) { - exitVisualMode(cm, vim); - } - if (operatorArgs.enterInsertMode) { - actions.enterInsertMode(cm, {}, vim); + exitVisualMode(cm); } } }, recordLastEdit: function(vim, inputState, actionCommand) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; if (macroModeState.inReplay) { return; } vim.lastEditInputState = inputState; vim.lastEditActionCommand = actionCommand; @@ -1272,8 +1293,13 @@ } var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0); var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat; - if (line < cm.firstLine() || line > cm.lastLine() ) { - return null; + var first = cm.firstLine(); + var last = cm.lastLine(); + // Vim cancels linewise motions that start on an edge and move beyond + // that edge. It does not cancel motions that do not start on an edge. + if ((line < first && cur.line == first) || + (line > last && cur.line == last)) { + return; } if(motionArgs.toFirstChar){ endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line)); @@ -1456,7 +1482,7 @@ return [start, end]; }, repeatLastCharacterSearch: function(cm, motionArgs) { - var lastSearch = getVimGlobalState().lastChararacterSearch; + var lastSearch = vimGlobalState.lastChararacterSearch; var repeat = motionArgs.repeat; var forward = motionArgs.forward === lastSearch.forward; var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1); @@ -1474,22 +1500,16 @@ var operators = { change: function(cm, operatorArgs, _vim, curStart, curEnd) { - getVimGlobalState().registerController.pushText( + vimGlobalState.registerController.pushText( operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd), operatorArgs.linewise); if (operatorArgs.linewise) { - // Delete starting at the first nonwhitespace character of the first - // line, instead of from the start of the first line. This way we get - // an indent when we get into insert mode. This behavior isn't quite - // correct because we should treat this as a completely new line, and - // indent should be whatever codemirror thinks is the right indent. - // But cm.indentLine doesn't seem work on empty lines. - // TODO: Fix the above. - curStart.ch = - findFirstNonWhiteSpaceCharacter(cm.getLine(curStart.line)); - // Insert an additional newline so that insert mode can start there. - // curEnd should be on the first character of the new line. - cm.replaceRange('\n', curStart, curEnd); + // Push the next line back down, if there is a next line. + var replacement = curEnd.line > cm.lastLine() ? '' : '\n'; + cm.replaceRange(replacement, curStart, curEnd); + cm.indentLine(curStart.line, 'smart'); + // null ch so setCursor moves to end of line. + curStart.ch = null; } else { // Exclude trailing whitespace if the range is not all whitespace. var text = cm.getRange(curStart, curEnd); @@ -1501,6 +1521,7 @@ } cm.replaceRange('', curStart, curEnd); } + actions.enterInsertMode(cm, {}, cm.state.vim); cm.setCursor(curStart); }, // delete is a javascript keyword. @@ -1512,7 +1533,7 @@ curStart.line--; curStart.ch = lineLength(cm, curStart.line); } - getVimGlobalState().registerController.pushText( + vimGlobalState.registerController.pushText( operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.replaceRange('', curStart, curEnd); @@ -1542,7 +1563,7 @@ cm.setCursor(curStart); cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); }, - swapcase: function(cm, _operatorArgs, _vim, curStart, curEnd, curOriginal) { + swapcase: function(cm, operatorArgs, _vim, curStart, curEnd, curOriginal) { var toSwap = cm.getRange(curStart, curEnd); var swapped = ''; for (var i = 0; i < toSwap.length; i++) { @@ -1551,10 +1572,12 @@ character.toUpperCase(); } cm.replaceRange(swapped, curStart, curEnd); - cm.setCursor(curOriginal); + if (!operatorArgs.shouldMoveCursor) { + cm.setCursor(curOriginal); + } }, yank: function(cm, operatorArgs, _vim, curStart, curEnd, curOriginal) { - getVimGlobalState().registerController.pushText( + vimGlobalState.registerController.pushText( operatorArgs.registerName, 'yank', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.setCursor(curOriginal); @@ -1568,7 +1591,7 @@ } var repeat = actionArgs.repeat; var forward = actionArgs.forward; - var jumpList = getVimGlobalState().jumpList; + var jumpList = vimGlobalState.jumpList; var mark = jumpList.move(cm, forward ? repeat : -repeat); var markPos = mark ? mark.find() : undefined; @@ -1594,7 +1617,7 @@ replayMacro: function(cm, actionArgs) { var registerName = actionArgs.selectedCharacter; var repeat = actionArgs.repeat; - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; if (registerName == '@') { registerName = macroModeState.latestRegister; } @@ -1604,18 +1627,19 @@ } }, exitMacroRecordMode: function() { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; macroModeState.toggle(); parseKeyBufferToRegister(macroModeState.latestRegister, macroModeState.macroKeyBuffer); }, enterMacroRecordMode: function(cm, actionArgs) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var registerName = actionArgs.selectedCharacter; macroModeState.toggle(cm, registerName); emptyMacroKeyBuffer(macroModeState); }, enterInsertMode: function(cm, actionArgs, vim) { + if (cm.getOption('readOnly')) { return; } vim.insertMode = true; vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1; var insertAt = (actionArgs) ? actionArgs.insertAt : null; @@ -1633,10 +1657,12 @@ // Handle Replace-mode as a special case of insert mode. cm.toggleOverwrite(true); cm.setOption('keyMap', 'vim-replace'); + CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"}); } else { cm.setOption('keyMap', 'vim-insert'); + CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"}); } - if (!getVimGlobalState().macroModeState.inReplay) { + if (!vimGlobalState.macroModeState.inReplay) { // Only record if not replaying. cm.on('change', onChange); cm.on('cursorActivity', onCursorActivity); @@ -1651,6 +1677,7 @@ // equal to the repeat times the size of the previous visual // operation. if (!vim.visualMode) { + cm.on('mousedown', exitVisualMode); vim.visualMode = true; vim.visualLine = !!actionArgs.linewise; if (vim.visualLine) { @@ -1675,6 +1702,7 @@ } else { cm.setSelection(curStart, curEnd); } + CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""}); } else { curStart = cm.getCursor('anchor'); curEnd = cm.getCursor('head'); @@ -1687,12 +1715,14 @@ curEnd.ch = cursorIsBefore(curStart, curEnd) ? lineLength(cm, curEnd.line) : 0; cm.setSelection(curStart, curEnd); + CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: "linewise"}); } else if (vim.visualLine && !actionArgs.linewise) { // v pressed in linewise visual mode. Switch to characterwise visual // mode instead of exiting visual mode. vim.visualLine = false; + CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"}); } else { - exitVisualMode(cm, vim); + exitVisualMode(cm); } } updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart @@ -1728,6 +1758,7 @@ }); }, newLineAndEnterInsertMode: function(cm, actionArgs, vim) { + vim.insertMode = true; var insertAt = cm.getCursor(); if (insertAt.line === cm.firstLine() && !actionArgs.after) { // Special case for inserting newline before start of document. @@ -1746,7 +1777,7 @@ }, paste: function(cm, actionArgs) { var cur = cm.getCursor(); - var register = getVimGlobalState().registerController.getRegister( + var register = vimGlobalState.registerController.getRegister( actionArgs.registerName); if (!register.text) { return; @@ -1832,7 +1863,7 @@ cm.replaceRange(replaceWithStr, curStart, curEnd); if(vim.visualMode){ cm.setCursor(curStart); - exitVisualMode(cm,vim); + exitVisualMode(cm); }else{ cm.setCursor(offsetCursor(curEnd, 0, -1)); } @@ -1989,7 +2020,9 @@ return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1'); } - function exitVisualMode(cm, vim) { + function exitVisualMode(cm) { + cm.off('mousedown', exitVisualMode); + var vim = cm.state.vim; vim.visualMode = false; vim.visualLine = false; var selectionStart = cm.getCursor('anchor'); @@ -2000,6 +2033,7 @@ // it's not supposed to be. cm.setCursor(clipCursorToContent(cm, selectionEnd)); } + CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); } // Remove any trailing newlines from the selection. For @@ -2113,12 +2147,11 @@ function recordJumpPosition(cm, oldCur, newCur) { if(!cursorEqual(oldCur, newCur)) { - getVimGlobalState().jumpList.add(cm, oldCur, newCur); + vimGlobalState.jumpList.add(cm, oldCur, newCur); } } function recordLastCharacterSearch(increment, args) { - var vimGlobalState = getVimGlobalState(); vimGlobalState.lastChararacterSearch.increment = increment; vimGlobalState.lastChararacterSearch.forward = args.forward; vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter; @@ -2572,10 +2605,10 @@ function SearchState() {} SearchState.prototype = { getQuery: function() { - return getVimGlobalState().query; + return vimGlobalState.query; }, setQuery: function(query) { - getVimGlobalState().query = query; + vimGlobalState.query = query; }, getOverlay: function() { return this.searchOverlay; @@ -2584,14 +2617,14 @@ this.searchOverlay = overlay; }, isReversed: function() { - return getVimGlobalState().isReversed; + return vimGlobalState.isReversed; }, setReversed: function(reversed) { - getVimGlobalState().isReversed = reversed; + vimGlobalState.isReversed = reversed; } }; function getSearchState(cm) { - var vim = getVimState(cm); + var vim = cm.state.vim; return vim.searchState_ || (vim.searchState_ = new SearchState()); } function dialog(cm, template, shortText, onClose, options) { @@ -2840,9 +2873,9 @@ }; Vim.ExCommandDispatcher.prototype = { processCommand: function(cm, input) { - var vim = getVimState(cm); + var vim = cm.state.vim; if (vim.visualMode) { - exitVisualMode(cm, vim); + exitVisualMode(cm); } var inputStream = new CodeMirror.StringStream(input); var params = {}; @@ -2921,7 +2954,7 @@ case '$': return cm.lastLine(); case '\'': - var mark = getVimState(cm).marks[inputStream.next()]; + var mark = cm.state.vim.marks[inputStream.next()]; if (mark && mark.find()) { return mark.find().line; } @@ -3031,7 +3064,7 @@ exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm); }, move: function(cm, params) { - commandDispatcher.processCommand(cm, getVimState(cm), { + commandDispatcher.processCommand(cm, cm.state.vim, { type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, @@ -3186,7 +3219,7 @@ return; } - var state = getVimState(cm); + var state = cm.state.vim; var stream = new CodeMirror.StringStream(params.argString.trim()); while (!stream.eol()) { stream.eatSpace(); @@ -3257,6 +3290,7 @@ function doReplace(cm, confirm, lineStart, lineEnd, searchCursor, query, replaceWith) { // Set up all the functions. + cm.state.vim.exMode = true; var done = false; var lastPos = searchCursor.from(); function replaceAll() { @@ -3291,7 +3325,8 @@ cm.focus(); if (lastPos) { cm.setCursor(lastPos); - var vim = getVimState(cm); + var vim = cm.state.vim; + vim.exMode = false; vim.lastHPos = vim.lastHSPos = lastPos.ch; } } @@ -3403,9 +3438,8 @@ CodeMirror.keyMap.vim = buildVimKeyMap(); function exitInsertMode(cm) { - var vim = getVimState(cm); - vim.insertMode = false; - var inReplay = getVimGlobalState().macroModeState.inReplay; + var vim = cm.state.vim; + var inReplay = vimGlobalState.macroModeState.inReplay; if (!inReplay) { cm.off('change', onChange); cm.off('cursorActivity', onCursorActivity); @@ -3419,8 +3453,10 @@ } delete vim.insertModeRepeat; cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true); + vim.insertMode = false; cm.setOption('keyMap', 'vim'); cm.toggleOverwrite(false); // exit replace mode if we were in it. + CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); } CodeMirror.keyMap['vim-insert'] = { @@ -3446,7 +3482,7 @@ function parseRegisterToKeyBuffer(macroModeState, registerName) { var match, key; - var register = getVimGlobalState().registerController.getRegister(registerName); + var register = vimGlobalState.registerController.getRegister(registerName); var text = register.toString(); var macroKeyBuffer = macroModeState.macroKeyBuffer; emptyMacroKeyBuffer(macroModeState); @@ -3462,7 +3498,7 @@ function parseKeyBufferToRegister(registerName, keyBuffer) { var text = keyBuffer.join(''); - getVimGlobalState().registerController.setRegisterText(registerName, text); + vimGlobalState.registerController.setRegisterText(registerName, text); } function emptyMacroKeyBuffer(macroModeState) { @@ -3490,7 +3526,7 @@ * Should only be active in insert mode. */ function onChange(_cm, changeObj) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; while (changeObj) { lastChange.expectCursorActivityForChange = true; @@ -3510,7 +3546,7 @@ * - Should only be active in insert mode. */ function onCursorActivity() { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; if (lastChange.expectCursorActivityForChange) { lastChange.expectCursorActivityForChange = false; @@ -3531,7 +3567,7 @@ * - For recording deletes in insert mode. */ function onKeyEventTargetKeyDown(e) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; var keyName = CodeMirror.keyName(e); function onKeyFound() { @@ -3553,7 +3589,7 @@ * corresponding enterInsertMode call was made with a count. */ function repeatLastEdit(cm, vim, repeat, repeatForInsert) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; macroModeState.inReplay = true; var isAction = !!vim.lastEditActionCommand; var cachedInputState = vim.inputState; @@ -3621,6 +3657,7 @@ } } + resetVimGlobalState(); return vimApi; }; // Initialize Vim and make it available as an API. diff --git a/applications/admin/static/codemirror/lib/codemirror.css b/applications/admin/static/codemirror/lib/codemirror.css index 52881f7d..4e300b2b 100644 --- a/applications/admin/static/codemirror/lib/codemirror.css +++ b/applications/admin/static/codemirror/lib/codemirror.css @@ -95,6 +95,7 @@ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} +.CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ @@ -117,6 +118,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; + -moz-box-sizing: content-box; + box-sizing: content-box; } .CodeMirror-sizer { position: relative; @@ -155,6 +158,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-gutter { white-space: normal; height: 100%; + -moz-box-sizing: content-box; + box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; @@ -192,6 +197,16 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} white-space: pre-wrap; word-break: normal; } +.CodeMirror-code pre { + border-right: 30px solid transparent; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; +} +.CodeMirror-wrap .CodeMirror-code pre { + border-right: none; + width: auto; +} .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; @@ -204,8 +219,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} overflow: auto; } -.CodeMirror-widget { -} +.CodeMirror-widget {} .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; @@ -213,7 +227,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-measure { position: absolute; - width: 100%; height: 0px; + width: 100%; + height: 0; overflow: hidden; visibility: hidden; } diff --git a/applications/admin/static/codemirror/lib/codemirror.js b/applications/admin/static/codemirror/lib/codemirror.js index ac0f3241..6cfc6ff9 100644 --- a/applications/admin/static/codemirror/lib/codemirror.js +++ b/applications/admin/static/codemirror/lib/codemirror.js @@ -1,4 +1,4 @@ -// CodeMirror version 3.14 +// CodeMirror version 3.18 // // CodeMirror is the only global var we claim window.CodeMirror = (function() { @@ -26,10 +26,11 @@ window.CodeMirror = (function() { // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent); var mac = ios || /Mac/.test(navigator.platform); - var windows = /windows/i.test(navigator.platform); + var windows = /win/i.test(navigator.platform); var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/); if (opera_version) opera_version = Number(opera_version[1]); + 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); @@ -305,15 +306,13 @@ window.CodeMirror = (function() { // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. function setGuttersForLineNumbers(options) { - var found = false; - for (var i = 0; i < options.gutters.length; ++i) { - if (options.gutters[i] == "CodeMirror-linenumbers") { - if (options.lineNumbers) found = true; - else options.gutters.splice(i--, 1); - } + var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + if (found == -1 && options.lineNumbers) { + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + } else if (found > -1 && !options.lineNumbers) { + options.gutters = options.gutters.slice(0); + options.gutters.splice(found, 1); } - if (!found && options.lineNumbers) - options.gutters.push("CodeMirror-linenumbers"); } // SCROLLBARS @@ -333,13 +332,19 @@ window.CodeMirror = (function() { d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; d.scrollbarV.firstChild.style.height = (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; - } else d.scrollbarV.style.display = ""; + } else { + d.scrollbarV.style.display = ""; + d.scrollbarV.firstChild.style.height = "0"; + } if (needsH) { d.scrollbarH.style.display = "block"; d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0"; d.scrollbarH.firstChild.style.width = (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px"; - } else d.scrollbarH.style.display = ""; + } else { + d.scrollbarH.style.display = ""; + d.scrollbarH.firstChild.style.width = "0"; + } if (needsH && needsV) { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px"; @@ -403,14 +408,21 @@ window.CodeMirror = (function() { // DISPLAY DRAWING - function updateDisplay(cm, changes, viewPort) { + function updateDisplay(cm, changes, viewPort, forced) { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; var visible = visibleLines(cm.display, cm.doc, viewPort); - for (;;) { - if (!updateDisplayInner(cm, changes, visible)) break; + for (var first = true;; first = false) { + var oldWidth = cm.display.scroller.clientWidth; + if (!updateDisplayInner(cm, changes, visible, forced)) break; updated = true; + changes = []; updateSelection(cm); updateScrollbars(cm); + if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) { + forced = true; + continue; + } + forced = false; // Clip forced viewport to actual scrollable area if (viewPort) @@ -419,7 +431,6 @@ window.CodeMirror = (function() { visible = visibleLines(cm.display, cm.doc, viewPort); if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo) break; - changes = []; } if (updated) { @@ -433,7 +444,7 @@ window.CodeMirror = (function() { // Uses a set of changes plus the current scroll position to // determine which DOM updates have to be made, and makes the // updates. - function updateDisplayInner(cm, changes, visible) { + function updateDisplayInner(cm, changes, visible, forced) { var display = cm.display, doc = cm.doc; if (!display.wrapper.clientWidth) { display.showingFrom = display.showingTo = doc.first; @@ -442,7 +453,7 @@ window.CodeMirror = (function() { } // Bail out if the visible area is already rendered and nothing changed. - if (changes.length == 0 && + if (!forced && changes.length == 0 && visible.from > display.showingFrom && visible.to < display.showingTo) return; @@ -455,7 +466,7 @@ window.CodeMirror = (function() { var positionsChangedFrom = Infinity; if (cm.options.lineNumbers) for (var i = 0; i < changes.length; ++i) - if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } + if (changes[i].diff && changes[i].from < positionsChangedFrom) { positionsChangedFrom = changes[i].from; } var end = doc.first + doc.size; var from = Math.max(visible.from - cm.options.viewportMargin, doc.first); @@ -495,7 +506,7 @@ window.CodeMirror = (function() { if (range.from >= range.to) intact.splice(i--, 1); else intactLines += range.to - range.from; } - if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) { + if (!forced && intactLines == to - from && from == display.showingFrom && to == display.showingTo) { updateViewOffset(cm); return; } @@ -520,6 +531,14 @@ window.CodeMirror = (function() { } display.showingFrom = from; display.showingTo = to; + updateHeightsInViewport(cm); + updateViewOffset(cm); + + return true; + } + + function updateHeightsInViewport(cm) { + var display = cm.display; var prevBottom = display.lineDiv.offsetTop; for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) { if (ie_lt8) { @@ -539,9 +558,6 @@ window.CodeMirror = (function() { widgets[i].height = widgets[i].node.offsetHeight; } } - updateViewOffset(cm); - - return true; } function updateViewOffset(cm) { @@ -607,7 +623,7 @@ window.CodeMirror = (function() { if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift(); if (lineIsHidden(cm.doc, line)) { if (line.height != 0) updateLineHeight(line, 0); - if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) { + if (line.widgets && cur && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) { var w = line.widgets[i]; if (w.showIfHidden) { var prev = cur.previousSibling; @@ -652,10 +668,11 @@ window.CodeMirror = (function() { } function buildLineElement(cm, line, lineNo, dims, reuse) { - var lineElement = lineContent(cm, line); + var built = buildLineContent(cm, line), lineElement = built.pre; var markers = line.gutterMarkers, display = cm.display, wrap; - if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets) + var bgClass = built.bgClass ? built.bgClass + " " + (line.bgClass || "") : line.bgClass; + if (!cm.options.lineNumbers && !markers && !bgClass && !line.wrapClass && !line.widgets) return lineElement; // Lines with gutter elements, widgets or a background class need @@ -670,10 +687,10 @@ window.CodeMirror = (function() { if (!/\bCodeMirror-linewidget\b/.test(n.className)) { reuse.removeChild(n); } else { - for (var i = 0, first = true; i < line.widgets.length; ++i) { + for (var i = 0; i < line.widgets.length; ++i) { var widget = line.widgets[i]; - if (!widget.above) { insertBefore = n; first = false; } if (widget.node == n.firstChild) { + if (!widget.above && !insertBefore) insertBefore = n; positionLineWidget(widget, n, reuse, dims); ++widgetsSeen; break; @@ -693,8 +710,8 @@ window.CodeMirror = (function() { wrap.appendChild(lineElement); } // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClass) - wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild); + 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: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"), @@ -866,9 +883,10 @@ window.CodeMirror = (function() { clearInterval(display.blinker); var on = true; display.cursor.style.visibility = display.otherCursor.style.visibility = ""; - display.blinker = setInterval(function() { - display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; - }, cm.options.cursorBlinkRate); + if (cm.options.cursorBlinkRate > 0) + display.blinker = setInterval(function() { + display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } // HIGHLIGHT WORKER @@ -919,8 +937,8 @@ window.CodeMirror = (function() { // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { - var minindent, minline, doc = cm.doc; - for (var search = n, lim = n - 100; search > lim; --search) { + var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100; + for (var search = n, lim = n - maxScan; search > lim; --search) { if (search <= doc.first) return doc.first; var line = getLine(doc, search - 1); if (line.stateAfter && (!precise || search <= doc.frontier)) return search; @@ -935,7 +953,7 @@ window.CodeMirror = (function() { function getStateBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; - if (!doc.mode.startState) return true; + if (!doc.mode.startState) return true; var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter; if (!state) state = startState(doc.mode); else state = copyState(doc.mode, state); @@ -960,17 +978,23 @@ window.CodeMirror = (function() { function measureChar(cm, line, ch, data, bias) { var dir = -1; data = data || measureLine(cm, line); + if (data.crude) { + var left = data.left + ch * data.width; + return {left: left, right: left + data.width, top: data.top, bottom: data.bottom}; + } for (var pos = ch;; pos += dir) { var r = data[pos]; if (r) break; if (dir < 0 && pos == 0) dir = 1; } - var rightV = (pos < ch || bias == "right") && r.topRight != null; + bias = pos > ch ? "left" : pos < ch ? "right" : bias; + if (bias == "left" && r.leftSide) r = r.leftSide; + else if (bias == "right" && r.rightSide) r = r.rightSide; return {left: pos < ch ? r.right : r.left, right: pos > ch ? r.left : r.right, - top: rightV ? r.topRight : r.top, - bottom: rightV ? r.bottomRight : r.bottom}; + top: r.top, + bottom: r.bottom}; } function findCachedMeasurement(cm, line) { @@ -979,7 +1003,7 @@ window.CodeMirror = (function() { var memo = cache[i]; if (memo.text == line.text && memo.markedSpans == line.markedSpans && cm.display.scroller.clientWidth == memo.width && - memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass) + memo.classes == line.textClass + "|" + line.wrapClass) return memo; } } @@ -999,15 +1023,18 @@ window.CodeMirror = (function() { var cache = cm.display.measureLineCache; var memo = {text: line.text, width: cm.display.scroller.clientWidth, markedSpans: line.markedSpans, measure: measure, - classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass}; + classes: line.textClass + "|" + line.wrapClass}; if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; else cache.push(memo); return measure; } function measureLineInner(cm, line) { + if (!cm.options.lineWrapping && line.text.length >= cm.options.crudeMeasuringFrom) + return crudelyMeasureLine(cm, line); + var display = cm.display, measure = emptyArray(line.text.length); - var pre = lineContent(cm, line, measure); + var pre = buildLineContent(cm, line, measure, true).pre; // IE does not cache element positions of inline elements between // calls to getBoundingClientRect. This makes the loop below, @@ -1043,52 +1070,64 @@ window.CodeMirror = (function() { if (ie_lt9 && display.measure.first != pre) removeChildrenAndAdd(display.measure, pre); - function categorizeVSpan(top, bot) { + function measureRect(rect) { + var top = rect.top - outer.top, bot = rect.bottom - outer.top; if (bot > maxBot) bot = maxBot; if (top < 0) top = 0; - for (var j = 0; j < vranges.length; j += 2) { - var rtop = vranges[j], rbot = vranges[j+1]; + for (var i = vranges.length - 2; i >= 0; i -= 2) { + var rtop = vranges[i], rbot = vranges[i+1]; if (rtop > bot || rbot < top) continue; if (rtop <= top && rbot >= bot || top <= rtop && bot >= rbot || Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) { - vranges[j] = Math.min(top, rtop); - vranges[j+1] = Math.max(bot, rbot); - return j; + vranges[i] = Math.min(top, rtop); + vranges[i+1] = Math.max(bot, rbot); + break; } } - vranges.push(top, bot); - return j; + if (i < 0) { i = vranges.length; vranges.push(top, bot); } + return {left: rect.left - outer.left, + right: rect.right - outer.left, + top: i, bottom: null}; + } + function finishRect(rect) { + rect.bottom = vranges[rect.top+1]; + rect.top = vranges[rect.top]; } for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) { - var size, node = cur; + var node = cur, rect = null; // A widget might wrap, needs special care if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) { if (cur.firstChild.nodeType == 1) node = cur.firstChild; - var rects = node.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1]; + var rects = node.getClientRects(); if (rects.length > 1) { - var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top); - var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top); - data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left, - top: vCatLeft, topRight: vCatRight}; - continue; + rect = data[i] = measureRect(rects[0]); + rect.rightSide = measureRect(rects[rects.length - 1]); } } - size = getRect(node); - var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top); - var right = size.right; - if (cur.measureRight) right = getRect(cur.measureRight).left; - data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat}; + if (!rect) rect = data[i] = measureRect(getRect(node)); + if (cur.measureRight) rect.right = getRect(cur.measureRight).left; + if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide)); } + removeChildren(cm.display.measure); for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) { - var vr = cur.top, vrRight = cur.topRight; - cur.top = vranges[vr]; cur.bottom = vranges[vr+1]; - if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; } + finishRect(cur); + if (cur.leftSide) finishRect(cur.leftSide); + if (cur.rightSide) finishRect(cur.rightSide); } return data; } + function crudelyMeasureLine(cm, line) { + var copy = new Line(line.text.slice(0, 100), null); + if (line.textClass) copy.textClass = line.textClass; + var measure = measureLineInner(cm, copy); + var left = measureChar(cm, copy, 0, measure, "left"); + var right = measureChar(cm, copy, 99, measure, "right"); + return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100}; + } + function measureLineWidth(cm, line) { var hasBadSpan = false; if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) { @@ -1096,9 +1135,10 @@ window.CodeMirror = (function() { if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true; } var cached = !hasBadSpan && findCachedMeasurement(cm, line); - if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right; + if (cached || line.text.length >= cm.options.crudeMeasuringFrom) + return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right; - var pre = lineContent(cm, line); + var pre = buildLineContent(cm, line, null, true).pre; var end = pre.appendChild(zeroWidthElement(cm.display.measure)); removeChildrenAndAdd(cm.display.measure, pre); return getRect(end).right - getRect(cm.display.lineDiv).left; @@ -1302,6 +1342,7 @@ window.CodeMirror = (function() { // An array of ranges of lines that have to be updated. See // updateDisplay. changes: [], + forceUpdate: false, updateInput: null, userSelChange: null, textChanged: null, @@ -1334,8 +1375,8 @@ window.CodeMirror = (function() { var coords = cursorCoords(cm, doc.sel.head); newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); } - if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) { - updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop); + if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) { + updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop, op.forceUpdate); if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop; } if (!updated && op.selectionChanged) updateSelection(cm); @@ -1432,6 +1473,10 @@ window.CodeMirror = (function() { 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.pasteIncoming && cm.state.fakedLastChar) { + input.value = input.value.substring(0, input.value.length - 1); + cm.state.fakedLastChar = false; + } var text = input.value; if (text == prevInput && posEq(sel.from, sel.to)) return false; if (ie && !ie_lt9 && cm.display.inputHasSelection === text) { @@ -1562,7 +1607,10 @@ window.CodeMirror = (function() { if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; if (e.keyCode == 16) cm.doc.sel.shift = false; })); - on(d.input, "input", bind(fastPoll, cm)); + on(d.input, "input", function() { + if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null; + fastPoll(cm); + }); on(d.input, "keydown", operation(cm, onKeyDown)); on(d.input, "keypress", operation(cm, onKeyPress)); on(d.input, "focus", bind(onFocus, cm)); @@ -1578,12 +1626,22 @@ window.CodeMirror = (function() { on(d.scroller, "dragover", drag_); on(d.scroller, "drop", operation(cm, onDrop)); } - on(d.scroller, "paste", function(e){ + on(d.scroller, "paste", function(e) { if (eventInWidget(d, e)) return; focusInput(cm); fastPoll(cm); }); on(d.input, "paste", function() { + // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206 + // Add a char to the end of textarea before paste occur so that + // selection doesn't span to the end of textarea. + if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) { + var start = d.input.selectionStart, end = d.input.selectionEnd; + d.input.value += "$"; + d.input.selectionStart = start; + d.input.selectionEnd = end; + cm.state.fakedLastChar = true; + } cm.state.pasteIncoming = true; fastPoll(cm); }); @@ -1647,6 +1705,7 @@ window.CodeMirror = (function() { if (captureMiddleClick) onContextMenu.call(cm, cm, e); return; case 2: + if (webkit) cm.state.lastMiddleDown = +new Date; if (start) extendSelection(cm.doc, start); setTimeout(bind(focusInput, cm), 20); e_preventDefault(e); @@ -1767,17 +1826,16 @@ window.CodeMirror = (function() { on(document, "mouseup", up); } - function clickInGutter(cm, e) { - var display = cm.display; + function gutterEvent(cm, e, type, prevent, signalfn) { try { var mX = e.clientX, mY = e.clientY; } catch(e) { return false; } + if (mX >= Math.floor(getRect(cm.display.gutters).right)) return false; + if (prevent) e_preventDefault(e); - if (mX >= Math.floor(getRect(display.gutters).right)) return false; - e_preventDefault(e); - if (!hasHandler(cm, "gutterClick")) return true; - + var display = cm.display; var lineBox = getRect(display.lineDiv); - if (mY > lineBox.bottom) return true; + + if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e); mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.options.gutters.length; ++i) { @@ -1785,11 +1843,19 @@ window.CodeMirror = (function() { if (g && getRect(g).right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.options.gutters[i]; - signalLater(cm, "gutterClick", cm, line, gutter, e); - break; + signalfn(cm, type, cm, line, gutter, e); + return e_defaultPrevented(e); } } - return true; + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) return false; + return gutterEvent(cm, e, "gutterContextMenu", false, signal); + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true, signalLater); } // Kludge to work around strange IE behavior where it'll sometimes @@ -1852,6 +1918,7 @@ window.CodeMirror = (function() { // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (opera) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); @@ -2050,8 +2117,8 @@ window.CodeMirror = (function() { function onKeyDown(e) { var cm = this; if (!cm.state.focused) onFocus(cm); - if (ie && e.keyCode == 27) { e.returnValue = false; } if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (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; @@ -2088,7 +2155,10 @@ window.CodeMirror = (function() { cm.state.focused = true; if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1) cm.display.wrapper.className += " CodeMirror-focused"; - resetInput(cm, true); + if (!cm.curOp) { + resetInput(cm, true); + if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730 + } } slowPoll(cm); restartBlink(cm); @@ -2105,8 +2175,9 @@ window.CodeMirror = (function() { var detectingSelectAll; function onContextMenu(cm, e) { + if (signalDOMEvent(cm, e, "contextmenu")) return; var display = cm.display, sel = cm.doc.sel; - if (eventInWidget(display, e)) return; + if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return; var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || opera) return; // Opera is difficult. @@ -2125,8 +2196,8 @@ window.CodeMirror = (function() { function prepareSelectAllHack() { if (display.input.selectionStart != null) { - var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value); - display.prevInput = " "; + var extval = display.input.value = "\u200b" + (posEq(sel.from, sel.to) ? "" : display.input.value); + display.prevInput = "\u200b"; display.input.selectionStart = 1; display.input.selectionEnd = extval.length; } } @@ -2260,6 +2331,7 @@ window.CodeMirror = (function() { } function makeChangeNoReadonly(doc, change, selUpdate) { + if (change.text.length == 1 && change.text[0] == "" && posEq(change.from, change.to)) return; var selAfter = computeSelAfterChange(doc, change, selUpdate); addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); @@ -2482,6 +2554,7 @@ window.CodeMirror = (function() { var sel = doc.sel; sel.goalColumn = null; + if (bias == null) bias = posLess(head, sel.head) ? -1 : 1; // Skip over atomic spans. if (checkAtomic || !posEq(anchor, sel.anchor)) anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push"); @@ -2755,7 +2828,7 @@ window.CodeMirror = (function() { function findWordAt(line, pos) { var start = pos.ch, end = pos.ch; if (line) { - if (pos.xRel < 0 || end == line.length) --start; else ++end; + if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; var startChar = line.charAt(start); var check = isWordChar(startChar) ? isWordChar : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} @@ -2796,7 +2869,7 @@ window.CodeMirror = (function() { removeKeyMap: function(map) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) - if ((typeof map == "string" ? maps[i].name : maps[i]) == map) { + if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) { maps.splice(i, 1); return true; } @@ -2860,6 +2933,7 @@ window.CodeMirror = (function() { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + if (ch == 0) return styles[2]; for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; @@ -2868,6 +2942,20 @@ window.CodeMirror = (function() { } }, + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) return mode; + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; + }, + + getHelper: function(pos, type) { + if (!helpers.hasOwnProperty(type)) return; + var help = helpers[type], mode = this.getModeAt(pos); + return mode[type] && help[mode[type]] || + mode.helperType && help[mode.helperType] || + help[mode.name]; + }, + getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); @@ -3097,24 +3185,25 @@ window.CodeMirror = (function() { updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop); }), - setSize: function(width, height) { + setSize: operation(null, function(width, height) { function interpret(val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; } if (width != null) this.display.wrapper.style.width = interpret(width); if (height != null) this.display.wrapper.style.height = interpret(height); - this.refresh(); - }, - - on: function(type, f) {on(this, type, f);}, - off: function(type, f) {off(this, type, f);}, + if (this.options.lineWrapping) + this.display.measureLineCache.length = this.display.measureLineCachePos = 0; + this.curOp.forceUpdate = true; + }), operation: function(f){return runInOp(this, f);}, refresh: operation(null, function() { + var badHeight = this.display.cachedTextHeight == null; clearCaches(this); updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop); regChange(this); + if (badHeight) estimateLineHeights(this); }), swapDoc: operation(null, function(doc) { @@ -3132,6 +3221,7 @@ window.CodeMirror = (function() { getScrollerElement: function(){return this.display.scroller;}, getGutterElement: function(){return this.display.gutters;} }; + eventMixin(CodeMirror); // OPTION DEFAULTS @@ -3214,6 +3304,7 @@ window.CodeMirror = (function() { option("historyEventDelay", 500); option("viewportMargin", 10, function(cm){cm.refresh();}, true); option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true); + option("crudeMeasuringFrom", 10000); option("moveInputWithCursor", true, function(cm, val) { if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0; }); @@ -3256,7 +3347,7 @@ window.CodeMirror = (function() { }; CodeMirror.getMode = function(options, spec) { - spec = CodeMirror.resolveMode(spec); + var spec = CodeMirror.resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) return CodeMirror.getMode(options, "text/plain"); var modeObj = mfactory(options, spec); @@ -3269,6 +3360,7 @@ window.CodeMirror = (function() { } } modeObj.name = spec.name; + return modeObj; }; @@ -3296,6 +3388,16 @@ window.CodeMirror = (function() { var initHooks = []; CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; + var helpers = CodeMirror.helpers = {}; + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {}; + helpers[type][name] = value; + }; + + // UTILITIES + + CodeMirror.isWordChar = isWordChar; + // MODE STATE HANDLING // Utility functions for working with state. Exported because modes @@ -3321,6 +3423,7 @@ window.CodeMirror = (function() { CodeMirror.innerMode = function(mode, state) { while (mode.innerMode) { var info = mode.innerMode(state); + if (!info || info.mode == mode) break; state = info.state; mode = info.mode; } @@ -3633,11 +3736,16 @@ window.CodeMirror = (function() { this.doc = doc; } CodeMirror.TextMarker = TextMarker; + eventMixin(TextMarker); TextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) startOperation(cm); + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) signalLater(this, "clear", found.from, found.to); + } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; @@ -3666,7 +3774,6 @@ window.CodeMirror = (function() { if (cm) reCheckSelection(cm); } if (withOp) endOperation(cm); - signalLater(this, "clear"); }; TextMarker.prototype.find = function() { @@ -3687,14 +3794,17 @@ window.CodeMirror = (function() { TextMarker.prototype.changed = function() { var pos = this.find(), cm = this.doc.cm; if (!pos || !cm) return; - var line = getLine(this.doc, pos.from.line); + if (this.type != "bookmark") pos = pos.from; + var line = getLine(this.doc, pos.line); clearCachedMeasurement(cm, line); - if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) { + if (pos.line >= cm.display.showingFrom && pos.line < cm.display.showingTo) { for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) { if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight); break; } - runInOp(cm, function() { cm.curOp.selectionChanged = true; }); + runInOp(cm, function() { + cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true; + }); } }; @@ -3767,7 +3877,7 @@ window.CodeMirror = (function() { } if (cm) { if (updateMaxLine) cm.curOp.updateMaxLine = true; - if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed) + if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed) regChange(cm, from.line, to.line + 1); if (marker.atomic) reCheckSelection(cm); } @@ -3785,6 +3895,7 @@ window.CodeMirror = (function() { } } CodeMirror.SharedTextMarker = SharedTextMarker; + eventMixin(SharedTextMarker); SharedTextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; @@ -4037,11 +4148,12 @@ window.CodeMirror = (function() { // LINE WIDGETS var LineWidget = CodeMirror.LineWidget = function(cm, node, options) { - for (var opt in options) if (options.hasOwnProperty(opt)) + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) this[opt] = options[opt]; this.cm = cm; this.node = node; }; + eventMixin(LineWidget); function widgetOperation(f) { return function() { var withOp = !this.cm.curOp; @@ -4056,7 +4168,9 @@ window.CodeMirror = (function() { if (no == null || !ws) return; for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); if (!ws.length) this.line.widgets = null; + var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop; updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this))); + if (aboveVisible) addToScrollPos(this.cm, 0, -this.height); regChange(this.cm, no, no + 1); }); LineWidget.prototype.changed = widgetOperation(function() { @@ -4080,10 +4194,12 @@ window.CodeMirror = (function() { var widget = new LineWidget(cm, node, options); if (widget.noHScroll) cm.display.alignWidgets = true; changeLine(cm, handle, function(line) { - (line.widgets || (line.widgets = [])).push(widget); + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) widgets.push(widget); + else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); widget.line = line; if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) { - var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop; + var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) addToScrollPos(cm, 0, widget.height); } @@ -4096,12 +4212,12 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function makeLine(text, markedSpans, estimateHeight) { - var line = {text: text}; - attachMarkedSpans(line, markedSpans); - line.height = estimateHeight ? estimateHeight(line) : 1; - return line; - } + var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + eventMixin(Line); function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; @@ -4131,8 +4247,7 @@ window.CodeMirror = (function() { while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; - // Webkit seems to refuse to render text nodes longer than 57444 characters - stream.pos = Math.min(text.length, stream.start + 50000); + stream.pos = text.length; style = null; } else { style = mode.token(stream, state); @@ -4143,7 +4258,12 @@ window.CodeMirror = (function() { } stream.start = stream.pos; } - if (curStart < stream.pos) f(stream.pos, curStyle); + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 characters + var pos = Math.min(stream.pos, curStart + 50000); + f(pos, curStyle); + curStart = pos; + } } function highlightLine(cm, line, state) { @@ -4201,20 +4321,30 @@ window.CodeMirror = (function() { } var styleToClassCache = {}; - function styleToClass(style) { + function interpretTokenStyle(style, builder) { if (!style) return null; + for (;;) { + var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/); + if (!lineClass) break; + style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (builder[prop] == null) + builder[prop] = lineClass[2]; + 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-")); } - function lineContent(cm, realLine, measure) { + function buildLineContent(cm, realLine, measure, copyWidgets) { var merged, line = realLine, empty = true; while (merged = collapsedSpanAtStart(line)) line = getLine(cm.doc, merged.find().from.line); - var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure, - measure: null, measuredSomething: false, cm: cm}; - if (line.textClass) builder.pre.className = line.textClass; + var builder = {pre: elt("pre"), col: 0, pos: 0, + measure: null, measuredSomething: false, cm: cm, + copyWidgets: copyWidgets}; do { if (line.text) empty = false; @@ -4251,12 +4381,15 @@ window.CodeMirror = (function() { } } + var textClass = builder.textClass ? builder.textClass + " " + (realLine.textClass || "") : realLine.textClass; + if (textClass) builder.pre.className = textClass; + signal(cm, "renderLine", cm, realLine, builder.pre); - return builder.pre; + return builder; } var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g; - function buildToken(builder, text, style, startStyle, endStyle) { + function buildToken(builder, text, style, startStyle, endStyle, title) { if (!text) return; if (!tokenSpecialChars.test(text)) { builder.col += text.length; @@ -4289,7 +4422,9 @@ window.CodeMirror = (function() { var fullStyle = style || ""; if (startStyle) fullStyle += startStyle; if (endStyle) fullStyle += endStyle; - return builder.pre.appendChild(elt("span", [content], fullStyle)); + var token = elt("span", [content], fullStyle); + if (title) token.title = title; + return builder.pre.appendChild(token); } builder.pre.appendChild(content); } @@ -4304,9 +4439,11 @@ window.CodeMirror = (function() { } else 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, start && startStyle, i == text.length - 1 && endStyle); + if (old) span.leftSide = old.leftSide || old; // In IE single-space nodes wrap differently than spaces // embedded in larger text nodes, except when set to // white-space: normal (issue #1268). @@ -4325,20 +4462,30 @@ window.CodeMirror = (function() { out += " "; return out; } - return function(builder, text, style, startStyle, endStyle) { - return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle); + return function(builder, text, style, startStyle, endStyle, title) { + return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title); }; } - function buildCollapsedSpan(builder, size, widget) { + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.replacedWith; if (widget) { - if (!builder.display) widget = widget.cloneNode(true); + if (builder.copyWidgets) widget = widget.cloneNode(true); + builder.pre.appendChild(widget); if (builder.measure) { - builder.measure[builder.pos] = size ? widget - : builder.pre.appendChild(zeroWidthElement(builder.cm.display.measure)); + if (size) { + builder.measure[builder.pos] = widget; + } else { + var elt = zeroWidthElement(builder.cm.display.measure); + if (marker.type == "bookmark" && !marker.insertLeft) + builder.measure[builder.pos] = builder.pre.appendChild(elt); + else if (builder.measure[builder.pos]) + return; + else + builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget); + } builder.measuredSomething = true; } - builder.pre.appendChild(widget); } builder.pos += size; } @@ -4349,17 +4496,17 @@ window.CodeMirror = (function() { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i = 1; i < styles.length; i+=2) - builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1])); + builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder)); return; } var len = allText.length, pos = 0, i = 1, text = "", style; - var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; for (;;) { if (nextChange == pos) { // Update current marker set - spanStyle = spanEndStyle = spanStartStyle = ""; + spanStyle = spanEndStyle = spanStartStyle = title = ""; collapsed = null; nextChange = Infinity; - var foundBookmark = null; + var foundBookmarks = []; for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (sp.from <= pos && (sp.to == null || sp.to > pos)) { @@ -4367,20 +4514,21 @@ window.CodeMirror = (function() { if (m.className) spanStyle += " " + m.className; 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)) collapsed = sp; } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; } - if (m.type == "bookmark" && sp.from == pos && m.replacedWith) - foundBookmark = m.replacedWith; + if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmarks.push(m); } if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos, - collapsed.from != null && collapsed.marker.replacedWith); + collapsed.marker, collapsed.from == null); if (collapsed.to == null) return collapsed.marker.find(); } - if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark); + if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j) + buildCollapsedSpan(builder, 0, foundBookmarks[j]); } if (pos >= len) break; @@ -4391,14 +4539,14 @@ window.CodeMirror = (function() { if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : ""); + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); - style = styleToClass(styles[i++]); + style = interpretTokenStyle(styles[i++], builder); } } } @@ -4421,7 +4569,7 @@ window.CodeMirror = (function() { // 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) - added.push(makeLine(text[i], spansFor(i), estimateHeight)); + added.push(new Line(text[i], spansFor(i), estimateHeight)); update(lastLine, lastLine.text, lastSpans); if (nlines) doc.remove(from.line, nlines); if (added.length) doc.insert(from.line, added); @@ -4430,8 +4578,8 @@ window.CodeMirror = (function() { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { for (var added = [], i = 1, e = text.length - 1; i < e; ++i) - added.push(makeLine(text[i], spansFor(i), estimateHeight)); - added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + added.push(new Line(text[i], spansFor(i), estimateHeight)); + added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added); } @@ -4442,7 +4590,7 @@ window.CodeMirror = (function() { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); for (var i = 1, e = text.length - 1, added = []; i < e; ++i) - added.push(makeLine(text[i], spansFor(i), estimateHeight)); + added.push(new Line(text[i], spansFor(i), estimateHeight)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1); doc.insert(from.line + 1, added); } @@ -4585,7 +4733,7 @@ window.CodeMirror = (function() { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); if (firstLine == null) firstLine = 0; - BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]); + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; @@ -4650,6 +4798,11 @@ window.CodeMirror = (function() { getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, getLineNumber: function(line) {return lineNo(line);}, + getLineHandleVisualStart: function(line) { + if (typeof line == "number") line = getLine(this, line); + return visualLine(this, line); + }, + lineCount: function() {return this.size;}, firstLine: function() {return this.first;}, lastLine: function() {return this.first + this.size - 1;}, @@ -4671,11 +4824,11 @@ window.CodeMirror = (function() { if (extend) extendSelection(this, pos); else setSelection(this, pos, pos); }), - setSelection: docOperation(function(anchor, head) { - setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor)); + setSelection: docOperation(function(anchor, head, bias) { + setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias); }), - extendSelection: docOperation(function(from, to) { - extendSelection(this, clipPos(this, from), to && clipPos(this, to)); + extendSelection: docOperation(function(from, to, bias) { + extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias); }), getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);}, @@ -4820,6 +4973,8 @@ window.CodeMirror = (function() { return function() {return method.apply(this.doc, arguments);}; })(Doc.prototype[prop]); + eventMixin(Doc); + function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) { @@ -4963,7 +5118,8 @@ window.CodeMirror = (function() { } function historyChangeFromChange(doc, change) { - var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + var from = { line: change.from.line, ch: change.from.ch }; + var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true); return histChange; @@ -5183,9 +5339,9 @@ window.CodeMirror = (function() { delayedCallbacks.push(bnd(arr[i])); } - function signalDOMEvent(cm, e) { - signal(cm, e.type, cm, e); - return e_defaultPrevented(e); + function signalDOMEvent(cm, e, override) { + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore; } function fireDelayed() { @@ -5202,6 +5358,11 @@ window.CodeMirror = (function() { CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal; + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + // MISC UTILITIES // Number of pixels added to scroller and sizer to hide scrollbar @@ -5353,11 +5514,18 @@ window.CodeMirror = (function() { spanAffectsWrapping = function(str, i) { return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1)); }; + else if (webkit && /Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent)) + spanAffectsWrapping = function(str, i) { + var code = str.charCodeAt(i - 1); + return code >= 8208 && code <= 8212; + }; else if (webkit) spanAffectsWrapping = function(str, i) { - if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) - return true; - return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1)); + if (i > 1 && str.charCodeAt(i - 1) == 45) { + 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)); }; var knownScrollbarWidth; @@ -5443,11 +5611,15 @@ window.CodeMirror = (function() { function iterateBidiSections(order, from, to, f) { if (!order) return f(from, to, "ltr"); + var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; - if (part.from < to && part.to > from || from == to && part.to == from) + if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + found = true; + } } + if (!found) f(from, to, "ltr"); } function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } @@ -5709,7 +5881,7 @@ window.CodeMirror = (function() { // THE END - CodeMirror.version = "3.14.0"; + CodeMirror.version = "3.18.0"; return CodeMirror; })(); diff --git a/applications/admin/static/codemirror/mode/apl/index.html b/applications/admin/static/codemirror/mode/apl/index.html index 119ff17f..f8282ac4 100644 --- a/applications/admin/static/codemirror/mode/apl/index.html +++ b/applications/admin/static/codemirror/mode/apl/index.html @@ -1,20 +1,32 @@ - - - - CodeMirror: APL mode - - - - - - - - -

CodeMirror: APL mode

+ + +
+

APL mode

+ + +

C++ example

+ +
+ +

Java example

+ +

Simple mode that tries to handle C-like languages as well as it @@ -99,5 +192,4 @@ void* zmq_thread_init(void* zmq_context, int signal_fd) { (C code), text/x-c++src (C++ code), text/x-java (Java code), text/x-csharp (C#).

- - +
diff --git a/applications/admin/static/codemirror/mode/clike/scala.html b/applications/admin/static/codemirror/mode/clike/scala.html index f3c7eea4..e9acc049 100644 --- a/applications/admin/static/codemirror/mode/clike/scala.html +++ b/applications/admin/static/codemirror/mode/clike/scala.html @@ -1,29 +1,30 @@ - - - - CodeMirror: C-like mode - - - - - - - - - + +CodeMirror: Scala mode + + + + + + + + + + +
+

Scala mode

+ + +

Cython mode

+ +
+ -

Configuration Options:

+

Configuration Options for Python mode:

  • version - 2/3 - The version of Python to recognize. Default is 2.
  • singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.
  • @@ -127,9 +178,10 @@ class ExampleClass(ParentClass):
  • doubleDelimiters - RegEx - Regular Expressoin for double delimiters matching, default :
    ^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
  • tripleDelimiters - RegEx - Regular Expression for triple delimiters matching, default :
    ^((//=)|(>>=)|(<<=)|(\\*\\*=))
  • identifiers - RegEx - Regular Expression for identifier, default :
    ^[_A-Za-z][_A-Za-z0-9]*
  • +
  • extra_keywords - list of string - List of extra words ton consider as keywords
  • +
  • extra_builtins - list of string - List of extra words ton consider as builtins
-

MIME types defined: text/x-python.

- - +

MIME types defined: text/x-python and text/x-cython.

+
diff --git a/applications/admin/static/codemirror/mode/python/python.js b/applications/admin/static/codemirror/mode/python/python.js index b623972b..802c2dd4 100644 --- a/applications/admin/static/codemirror/mode/python/python.js +++ b/applications/admin/static/codemirror/mode/python/python.js @@ -36,6 +36,12 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var py3 = {'builtins': ['ascii', 'bytes', 'exec', 'print'], 'keywords': ['nonlocal', 'False', 'True', 'None']}; + if(parserConf.extra_keywords != undefined){ + commonkeywords = commonkeywords.concat(parserConf.extra_keywords); + } + if(parserConf.extra_builtins != undefined){ + commonBuiltins = commonBuiltins.concat(parserConf.extra_builtins); + } if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) { commonkeywords = commonkeywords.concat(py3.keywords); commonBuiltins = commonBuiltins.concat(py3.builtins); @@ -145,6 +151,9 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } if (stream.match(identifiers)) { + if (state.lastToken == 'def' || state.lastToken == 'class') { + return 'def'; + } return 'variable'; } @@ -252,7 +261,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { // Handle '.' connected identifiers if (current === '.') { style = stream.match(identifiers, false) ? null : ERRORCLASS; - if (style === null && state.lastToken === 'meta') { + if (style === null && state.lastStyle === 'meta') { // Apply 'meta' style to '.' connected identifiers when // appropriate. style = 'meta'; @@ -266,7 +275,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } if ((style === 'variable' || style === 'builtin') - && state.lastToken === 'meta') { + && state.lastStyle === 'meta') { style = 'meta'; } @@ -307,6 +316,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return { tokenize: tokenBase, scopes: [{offset:basecolumn || 0, type:'py'}], + lastStyle: null, lastToken: null, lambda: false, dedent: 0 @@ -316,12 +326,16 @@ CodeMirror.defineMode("python", function(conf, parserConf) { token: function(stream, state) { var style = tokenLexer(stream, state); - state.lastToken = style; + state.lastStyle = style; - if (stream.eol() && stream.lambda) { - state.lambda = false; + var current = stream.current(); + if (current && style) { + state.lastToken = current; } + if (stream.eol() && state.lambda) { + state.lambda = false; + } return style; }, @@ -333,9 +347,22 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return state.scopes[0].offset; }, - lineComment: "#" + lineComment: "#", + fold: "indent" }; return external; }); CodeMirror.defineMIME("text/x-python", "python"); + +(function() { + "use strict"; + var words = function(str){return str.split(' ');}; + + CodeMirror.defineMIME("text/x-cython", { + name: "python", + extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ + "extern gil include nogil property public"+ + "readonly struct union DEF IF ELIF ELSE") + }); +})(); diff --git a/applications/admin/static/codemirror/mode/q/index.html b/applications/admin/static/codemirror/mode/q/index.html index 303ec1d3..78ed3d88 100644 --- a/applications/admin/static/codemirror/mode/q/index.html +++ b/applications/admin/static/codemirror/mode/q/index.html @@ -1,17 +1,31 @@ - - - - CodeMirror: Q mode - - - - - - - - -

CodeMirror: Q mode

+ +CodeMirror: Q mode + + + + + + + + + + +
+

Q mode

+

MIME types defined: text/x-sh.

+
diff --git a/applications/admin/static/codemirror/mode/sieve/LICENSE b/applications/admin/static/codemirror/mode/sieve/LICENSE deleted file mode 100644 index 8a74612c..00000000 --- a/applications/admin/static/codemirror/mode/sieve/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2012 Thomas Schmid - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/applications/admin/static/codemirror/mode/sieve/index.html b/applications/admin/static/codemirror/mode/sieve/index.html index 8b549815..9d814a9d 100644 --- a/applications/admin/static/codemirror/mode/sieve/index.html +++ b/applications/admin/static/codemirror/mode/sieve/index.html @@ -1,17 +1,30 @@ - - - - CodeMirror: Sieve (RFC5228) mode - - - - - - - -

CodeMirror: Sieve (RFC5228) mode

-

   

MIME type defined: text/x-vb.

- + diff --git a/applications/admin/static/codemirror/mode/vbscript/index.html b/applications/admin/static/codemirror/mode/vbscript/index.html index 8c86f9ef..9b506b79 100644 --- a/applications/admin/static/codemirror/mode/vbscript/index.html +++ b/applications/admin/static/codemirror/mode/vbscript/index.html @@ -1,16 +1,30 @@ - - - - CodeMirror: VBScript mode - - - - - - - -

CodeMirror: VBScript mode

+ +CodeMirror: VBScript mode + + + + + + + + + +
+

VBScript mode

+