diff --git a/gulliver/js/codemirror/addon/dialog/dialog.css b/gulliver/js/codemirror/addon/dialog/dialog.css new file mode 100644 index 000000000..2e7c0fc9b --- /dev/null +++ b/gulliver/js/codemirror/addon/dialog/dialog.css @@ -0,0 +1,32 @@ +.CodeMirror-dialog { + position: absolute; + left: 0; right: 0; + background: white; + z-index: 15; + padding: .1em .8em; + overflow: hidden; + color: #333; +} + +.CodeMirror-dialog-top { + border-bottom: 1px solid #eee; + top: 0; +} + +.CodeMirror-dialog-bottom { + border-top: 1px solid #eee; + bottom: 0; +} + +.CodeMirror-dialog input { + border: none; + outline: none; + background: transparent; + width: 20em; + color: inherit; + font-family: monospace; +} + +.CodeMirror-dialog button { + font-size: 70%; +} diff --git a/gulliver/js/codemirror/addon/dialog/dialog.js b/gulliver/js/codemirror/addon/dialog/dialog.js new file mode 100644 index 000000000..71e228744 --- /dev/null +++ b/gulliver/js/codemirror/addon/dialog/dialog.js @@ -0,0 +1,80 @@ +// Open simple dialogs on top of an editor. Relies on dialog.css. + +(function() { + function dialogDiv(cm, template, bottom) { + var wrap = cm.getWrapperElement(); + var dialog; + dialog = wrap.appendChild(document.createElement("div")); + if (bottom) { + dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom"; + } else { + dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; + } + dialog.innerHTML = template; + return dialog; + } + + CodeMirror.defineExtension("openDialog", function(template, callback, options) { + var dialog = dialogDiv(this, template, options && options.bottom); + var closed = false, me = this; + function close() { + if (closed) return; + closed = true; + dialog.parentNode.removeChild(dialog); + } + var inp = dialog.getElementsByTagName("input")[0], button; + if (inp) { + CodeMirror.on(inp, "keydown", function(e) { + if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } + if (e.keyCode == 13 || e.keyCode == 27) { + CodeMirror.e_stop(e); + close(); + me.focus(); + if (e.keyCode == 13) callback(inp.value); + } + }); + if (options && options.onKeyUp) { + CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);}); + } + if (options && options.value) inp.value = options.value; + inp.focus(); + CodeMirror.on(inp, "blur", close); + } else if (button = dialog.getElementsByTagName("button")[0]) { + CodeMirror.on(button, "click", function() { + close(); + me.focus(); + }); + button.focus(); + CodeMirror.on(button, "blur", close); + } + return close; + }); + + CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { + var dialog = dialogDiv(this, template, options && options.bottom); + var buttons = dialog.getElementsByTagName("button"); + var closed = false, me = this, blurring = 1; + function close() { + if (closed) return; + closed = true; + dialog.parentNode.removeChild(dialog); + me.focus(); + } + buttons[0].focus(); + for (var i = 0; i < buttons.length; ++i) { + var b = buttons[i]; + (function(callback) { + CodeMirror.on(b, "click", function(e) { + CodeMirror.e_preventDefault(e); + close(); + if (callback) callback(me); + }); + })(callbacks[i]); + CodeMirror.on(b, "blur", function() { + --blurring; + setTimeout(function() { if (blurring <= 0) close(); }, 200); + }); + CodeMirror.on(b, "focus", function() { ++blurring; }); + } + }); +})(); diff --git a/gulliver/js/codemirror/addon/display/placeholder.js b/gulliver/js/codemirror/addon/display/placeholder.js new file mode 100644 index 000000000..f85f2df12 --- /dev/null +++ b/gulliver/js/codemirror/addon/display/placeholder.js @@ -0,0 +1,54 @@ +(function() { + CodeMirror.defineOption("placeholder", "", function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + cm.on("focus", onFocus); + cm.on("blur", onBlur); + cm.on("change", onChange); + onChange(cm); + } else if (!val && prev) { + cm.off("focus", onFocus); + cm.off("blur", onBlur); + cm.off("change", onChange); + clearPlaceholder(cm); + var wrapper = cm.getWrapperElement(); + wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); + } + + if (val && !cm.hasFocus()) onBlur(cm); + }); + + function clearPlaceholder(cm) { + if (cm._placeholder) { + cm._placeholder.parentNode.removeChild(cm._placeholder); + cm._placeholder = null; + } + } + function setPlaceholder(cm) { + clearPlaceholder(cm); + var elt = cm._placeholder = document.createElement("pre"); + elt.style.cssText = "height: 0; overflow: visible"; + elt.className = "CodeMirror-placeholder"; + elt.appendChild(document.createTextNode(cm.getOption("placeholder"))); + cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); + } + + function onFocus(cm) { + clearPlaceholder(cm); + } + function onBlur(cm) { + if (isEmpty(cm)) setPlaceholder(cm); + } + function onChange(cm) { + var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); + wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); + + if (cm.hasFocus()) return; + if (empty) setPlaceholder(cm); + else clearPlaceholder(cm); + } + + function isEmpty(cm) { + return (cm.lineCount() === 1) && (cm.getLine(0) === ""); + } +})(); diff --git a/gulliver/js/codemirror/addon/edit/closebrackets.js b/gulliver/js/codemirror/addon/edit/closebrackets.js new file mode 100644 index 000000000..b46caca02 --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/closebrackets.js @@ -0,0 +1,52 @@ +(function() { + var DEFAULT_BRACKETS = "()[]{}''\"\""; + var SPACE_CHAR_REGEX = /\s/; + + CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { + var wasOn = old && old != CodeMirror.Init; + if (val && !wasOn) + cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS)); + else if (!val && wasOn) + cm.removeKeyMap("autoCloseBrackets"); + }); + + function buildKeymap(pairs) { + var map = { + name : "autoCloseBrackets", + Backspace: function(cm) { + if (cm.somethingSelected()) return CodeMirror.Pass; + var cur = cm.getCursor(), line = cm.getLine(cur.line); + if (cur.ch && cur.ch < line.length && + pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) + cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); + else + return CodeMirror.Pass; + } + }; + var closingBrackets = []; + for (var i = 0; i < pairs.length; i += 2) (function(left, right) { + if (left != right) closingBrackets.push(right); + function surround(cm) { + var selection = cm.getSelection(); + cm.replaceSelection(left + selection + right); + } + function maybeOverwrite(cm) { + var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); + if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass; + else cm.execCommand("goCharRight"); + } + map["'" + left + "'"] = function(cm) { + 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); + if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) + cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); + else + return CodeMirror.Pass; + }; + if (left != right) map["'" + right + "'"] = maybeOverwrite; + })(pairs.charAt(i), pairs.charAt(i + 1)); + return map; + } +})(); diff --git a/gulliver/js/codemirror/addon/edit/closetag.js b/gulliver/js/codemirror/addon/edit/closetag.js new file mode 100644 index 000000000..54fa19546 --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/closetag.js @@ -0,0 +1,85 @@ +/** + * Tag-closer extension for CodeMirror. + * + * This extension adds an "autoCloseTags" option that can be set to + * either true to get the default behavior, or an object to further + * configure its behavior. + * + * These are supported options: + * + * `whenClosing` (default true) + * Whether to autoclose when the '/' of a closing tag is typed. + * `whenOpening` (default true) + * Whether to autoclose the tag when the final '>' of an opening + * tag is typed. + * `dontCloseTags` (default is empty tags for HTML, none for XML) + * An array of tag names that should not be autoclosed. + * `indentTags` (default is block tags for HTML, none for XML) + * An array of tag names that should, when opened, cause a + * blank line to be added inside the tag, and the blank line and + * closing line to be indented. + * + * See demos/closetag.html for a usage example. + */ + +(function() { + CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { + if (val && (old == CodeMirror.Init || !old)) { + var map = {name: "autoCloseTags"}; + if (typeof val != "object" || val.whenClosing) + map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); }; + if (typeof val != "object" || val.whenOpening) + map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); }; + cm.addKeyMap(map); + } else if (!val && (old != CodeMirror.Init && old)) { + cm.removeKeyMap("autoCloseTags"); + } + }); + + var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", + "source", "track", "wbr"]; + 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) { + 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; + + 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("/") > -1 || + 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; + } + return CodeMirror.Pass; + } + + function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; + } +})(); diff --git a/gulliver/js/codemirror/addon/edit/continuecomment.js b/gulliver/js/codemirror/addon/edit/continuecomment.js new file mode 100644 index 000000000..308026229 --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/continuecomment.js @@ -0,0 +1,44 @@ +(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/gulliver/js/codemirror/addon/edit/continuelist.js b/gulliver/js/codemirror/addon/edit/continuelist.js new file mode 100644 index 000000000..fb1fc38ba --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/continuelist.js @@ -0,0 +1,25 @@ +(function() { + 'use strict'; + + var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/, + unorderedBullets = '*+-'; + + CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { + var pos = cm.getCursor(), + inList = cm.getStateAfter(pos.line).list, + match; + + if (!inList || !(match = cm.getLine(pos.line).match(listRE))) { + cm.execCommand('newlineAndIndent'); + return; + } + + var indent = match[1], after = match[4]; + var bullet = unorderedBullets.indexOf(match[2]) >= 0 + ? match[2] + : (parseInt(match[3], 10) + 1) + '.'; + + cm.replaceSelection('\n' + indent + bullet + after, 'end'); + }; + +}()); diff --git a/gulliver/js/codemirror/addon/edit/matchbrackets.js b/gulliver/js/codemirror/addon/edit/matchbrackets.js new file mode 100644 index 000000000..f4925b725 --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/matchbrackets.js @@ -0,0 +1,74 @@ +(function() { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxLineLen = 1000; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; + function findMatchingBracket(cm) { + var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; + var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; + if (!match) return null; + var forward = match.charAt(1) == ">", d = forward ? 1 : -1; + var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type; + + var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; + function scan(line, lineNo, start) { + if (!line.text) return; + var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; + if (start != null) pos = start + d; + for (; pos != end; pos += d) { + var ch = line.text.charAt(pos); + if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) { + var match = matching[ch]; + if (match.charAt(1) == ">" == forward) stack.push(ch); + else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; + else if (!stack.length) return {pos: pos, match: true}; + } + } + } + for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { + if (i == cur.line) found = scan(line, i, pos); + else found = scan(cm.getLineHandle(i), i); + if (found) break; + } + return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match}; + } + + function matchBrackets(cm, autoclear) { + var found = findMatchingBracket(cm); + if (!found || cm.getLine(found.from.line).length > maxLineLen || + found.to && cm.getLine(found.to.line).length > maxLineLen) + return; + + var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); + var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textare whever this fires. + if (ie_lt8 && cm.state.focused) cm.display.input.focus(); + var clear = function() { + cm.operation(function() { one.clear(); two && two.clear(); }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + + var currentlyHighlighted = null; + function doMatchBrackets(cm) { + cm.operation(function() { + if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} + if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false); + }); + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val) { + if (val) cm.on("cursorActivity", doMatchBrackets); + else cm.off("cursorActivity", doMatchBrackets); + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);}); +})(); diff --git a/gulliver/js/codemirror/addon/fold/brace-fold.js b/gulliver/js/codemirror/addon/fold/brace-fold.js new file mode 100644 index 000000000..aad6e0141 --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/brace-fold.js @@ -0,0 +1,31 @@ +CodeMirror.braceRangeFinder = function(cm, start) { + var line = start.line, lineText = cm.getLine(line); + var at = lineText.length, startChar, tokenType; + for (;;) { + var found = lineText.lastIndexOf("{", at); + if (found < start.ch) break; + tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; + if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } + at = found - 1; + } + if (startChar == null || lineText.lastIndexOf("}") > startChar) return; + var count = 1, lastLine = cm.lineCount(), end, endCh; + outer: for (var i = line + 1; i < lastLine; ++i) { + var text = cm.getLine(i), pos = 0; + for (;;) { + var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); + if (nextOpen < 0) nextOpen = text.length; + if (nextClose < 0) nextClose = text.length; + pos = Math.min(nextOpen, nextClose); + if (pos == text.length) break; + if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { + if (pos == nextOpen) ++count; + else if (!--count) { end = i; endCh = pos; break outer; } + } + ++pos; + } + } + if (end == null || end == line + 1) return; + return {from: CodeMirror.Pos(line, startChar + 1), + to: CodeMirror.Pos(end, endCh)}; +}; diff --git a/gulliver/js/codemirror/addon/fold/foldcode.js b/gulliver/js/codemirror/addon/fold/foldcode.js new file mode 100644 index 000000000..b8b4b0da9 --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/foldcode.js @@ -0,0 +1,32 @@ +CodeMirror.newFoldFunction = function(rangeFinder, widget) { + if (widget == null) widget = "\u2194"; + if (typeof widget == "string") { + var text = document.createTextNode(widget); + widget = document.createElement("span"); + widget.appendChild(text); + widget.className = "CodeMirror-foldmarker"; + } + + return function(cm, pos) { + if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); + var range = rangeFinder(cm, pos); + if (!range) return; + + var present = cm.findMarksAt(range.from), cleared = 0; + for (var i = 0; i < present.length; ++i) { + if (present[i].__isFold) { + ++cleared; + present[i].clear(); + } + } + if (cleared) return; + + var myWidget = widget.cloneNode(true); + CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();}); + var myRange = cm.markText(range.from, range.to, { + replacedWith: myWidget, + clearOnEnter: true, + __isFold: true + }); + }; +}; diff --git a/gulliver/js/codemirror/addon/fold/indent-fold.js b/gulliver/js/codemirror/addon/fold/indent-fold.js new file mode 100644 index 000000000..94a0a1ffa --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/indent-fold.js @@ -0,0 +1,11 @@ +CodeMirror.indentRangeFinder = 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) { + var curLine = cm.getLine(i); + if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent && + CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent) + return {from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(i, curLine.length)}; + } +}; diff --git a/gulliver/js/codemirror/addon/fold/xml-fold.js b/gulliver/js/codemirror/addon/fold/xml-fold.js new file mode 100644 index 000000000..79c524d48 --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/xml-fold.js @@ -0,0 +1,64 @@ +CodeMirror.tagRangeFinder = (function() { + 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"); + + return function(cm, start) { + var line = start.line, ch = start.ch, lineText = cm.getLine(line); + + function nextLine() { + if (line >= cm.lastLine()) return; + ch = 0; + lineText = cm.getLine(++line); + return true; + } + function toTagEnd() { + for (;;) { + var gt = lineText.indexOf(">", ch); + if (gt == -1) { if (nextLine()) continue; else return; } + var lastSlash = lineText.lastIndexOf("/", gt); + var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt)); + ch = gt + 1; + return selfClose ? "selfClose" : "regular"; + } + } + function toNextTag() { + for (;;) { + xmlTagStart.lastIndex = ch; + var found = xmlTagStart.exec(lineText); + if (!found) { if (nextLine()) continue; else return; } + ch = found.index + found[0].length; + return found; + } + } + + var stack = [], startCh; + for (;;) { + var openTag = toNextTag(), end; + if (!openTag || line != start.line || !(end = toTagEnd())) return; + if (!openTag[1] && end != "selfClose") { + stack.push(openTag[2]); + startCh = ch; + break; + } + } + + for (;;) { + var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0); + if (!next || !(end = toTagEnd())) return; + if (end == "selfClose") continue; + if (next[1]) { // closing tag + for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) { + stack.length = i; + break; + } + if (!stack.length) return { + from: CodeMirror.Pos(start.line, startCh), + to: CodeMirror.Pos(tagLine, tagCh) + }; + } else { // opening tag + stack.push(next[2]); + } + } + }; +})(); diff --git a/gulliver/js/codemirror/addon/hint/html-hint.js b/gulliver/js/codemirror/addon/hint/html-hint.js new file mode 100755 index 000000000..8b5dc6f00 --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/html-hint.js @@ -0,0 +1,582 @@ +(function () { + function htmlHint(editor, htmlStructure, getToken) { + var cur = editor.getCursor(); + var token = getToken(editor, cur); + var keywords = []; + var i = 0; + var j = 0; + var k = 0; + var from = {line: cur.line, ch: cur.ch}; + var to = {line: cur.line, ch: cur.ch}; + var flagClean = true; + + var text = editor.getRange({line: 0, ch: 0}, cur); + + var open = text.lastIndexOf('<'); + var close = text.lastIndexOf('>'); + var tokenString = token.string.replace("<",""); + + if(open > close) { + var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); + if(last == "<") { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push(htmlStructure[i].tag); + } + from.ch = token.start + 1; + } else { + var counter = 0; + var found = function(token, type, position) { + counter++; + if(counter > 50) return; + if(token.type == type) { + return token; + } else { + position.ch = token.start; + var newToken = editor.getTokenAt(position); + return found(newToken, type, position); + } + }; + + var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); + var node = nodeToken.string.substring(1); + + if(token.type === null && token.string.trim() === "") { + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); + } + + for(k = 0; k < globalAttributes.length; k++) { + keywords.push(globalAttributes[k].key + "=\"\" "); + } + } + } + } else if(token.type == "string") { + tokenString = tokenString.substring(1, tokenString.length - 1); + var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); + var attribute = attributeToken.string; + + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + if(htmlStructure[i].attr[j].key == attribute) { + for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) { + keywords.push(htmlStructure[i].attr[j].values[k]); + } + } + } + + for(j = 0; j < globalAttributes.length; j++) { + if(globalAttributes[j].key == attribute) { + for(k = 0; k < globalAttributes[j].values.length; k++) { + keywords.push(globalAttributes[j].values[k]); + } + } + } + } + } + from.ch = token.start + 1; + } else if(token.type == "attribute") { + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); + } + + for(k = 0; k < globalAttributes.length; k++) { + keywords.push(globalAttributes[k].key + "=\"\" "); + } + } + } + from.ch = token.start; + } else if(token.type == "tag") { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push(htmlStructure[i].tag); + } + + from.ch = token.start + 1; + } + } + } else { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push("<" + htmlStructure[i].tag); + } + + tokenString = ("<" + tokenString).trim(); + from.ch = token.start; + } + + if(flagClean === true && tokenString.trim() === "") { + flagClean = false; + } + + if(flagClean) { + keywords = cleanResults(tokenString, keywords); + } + + return {list: keywords, from: from, to: to}; + } + + + var cleanResults = function(text, keywords) { + var results = []; + var i = 0; + + for(i = 0; i < keywords.length; i++) { + if(keywords[i].substring(0, text.length) == text) { + results.push(keywords[i]); + } + } + + return results; + }; + + var htmlStructure = [ + {tag: '!DOCTYPE', attr: []}, + {tag: 'a', attr: [ + {key: 'href', values: ["#"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]}, + {key: 'ping', values: [""]}, + {key: 'media', values: ["#"]}, + {key: 'hreflang', values: ["en","es"]}, + {key: 'type', values: []} + ]}, + {tag: 'abbr', attr: []}, + {tag: 'acronym', attr: []}, + {tag: 'address', attr: []}, + {tag: 'applet', attr: []}, + {tag: 'area', attr: [ + {key: 'alt', values: [""]}, + {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]}, + {key: 'shape', values: ["default","rect","circle","poly"]}, + {key: 'href', values: ["#"]}, + {key: 'target', values: ["#"]}, + {key: 'ping', values: []}, + {key: 'media', values: []}, + {key: 'hreflang', values: []}, + {key: 'type', values: []} + + ]}, + {tag: 'article', attr: []}, + {tag: 'aside', attr: []}, + {tag: 'audio', attr: [ + {key: 'src', values: []}, + {key: 'crossorigin', values: ["anonymous","use-credentials"]}, + {key: 'preload', values: ["none","metadata","auto"]}, + {key: 'autoplay', values: ["","autoplay"]}, + {key: 'mediagroup', values: []}, + {key: 'loop', values: ["","loop"]}, + {key: 'controls', values: ["","controls"]} + ]}, + {tag: 'b', attr: []}, + {tag: 'base', attr: [ + {key: 'href', values: ["#"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]} + ]}, + {tag: 'basefont', attr: []}, + {tag: 'bdi', attr: []}, + {tag: 'bdo', attr: []}, + {tag: 'big', attr: []}, + {tag: 'blockquote', attr: [ + {key: 'cite', values: ["http://"]} + ]}, + {tag: 'body', attr: []}, + {tag: 'br', attr: []}, + {tag: 'button', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'formaction', values: []}, + {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'formmethod', values: ["get","post","put","delete"]}, + {key: 'formnovalidate', values: ["","novalidate"]}, + {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, + {key: 'name', values: []}, + {key: 'type', values: ["submit","reset","button"]}, + {key: 'value', values: []} + ]}, + {tag: 'canvas', attr: [ + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'caption', attr: []}, + {tag: 'center', attr: []}, + {tag: 'cite', attr: []}, + {tag: 'code', attr: []}, + {tag: 'col', attr: [ + {key: 'span', values: []} + ]}, + {tag: 'colgroup', attr: [ + {key: 'span', values: []} + ]}, + {tag: 'command', attr: [ + {key: 'type', values: ["command","checkbox","radio"]}, + {key: 'label', values: []}, + {key: 'icon', values: []}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'checked', values: ["","checked"]}, + {key: 'radiogroup', values: []}, + {key: 'command', values: []}, + {key: 'title', values: []} + ]}, + {tag: 'data', attr: [ + {key: 'value', values: []} + ]}, + {tag: 'datagrid', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'multiple', values: ["","multiple"]} + ]}, + {tag: 'datalist', attr: [ + {key: 'data', values: []} + ]}, + {tag: 'dd', attr: []}, + {tag: 'del', attr: [ + {key: 'cite', values: []}, + {key: 'datetime', values: []} + ]}, + {tag: 'details', attr: [ + {key: 'open', values: ["","open"]} + ]}, + {tag: 'dfn', attr: []}, + {tag: 'dir', attr: []}, + {tag: 'div', attr: [ + {key: 'id', values: []}, + {key: 'class', values: []}, + {key: 'style', values: []} + ]}, + {tag: 'dl', attr: []}, + {tag: 'dt', attr: []}, + {tag: 'em', attr: []}, + {tag: 'embed', attr: [ + {key: 'src', values: []}, + {key: 'type', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'eventsource', attr: [ + {key: 'src', values: []} + ]}, + {tag: 'fieldset', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'name', values: []} + ]}, + {tag: 'figcaption', attr: []}, + {tag: 'figure', attr: []}, + {tag: 'font', attr: []}, + {tag: 'footer', attr: []}, + {tag: 'form', attr: [ + {key: 'accept-charset', values: ["UNKNOWN","utf-8"]}, + {key: 'action', values: []}, + {key: 'autocomplete', values: ["on","off"]}, + {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'method', values: ["get","post","put","delete","dialog"]}, + {key: 'name', values: []}, + {key: 'novalidate', values: ["","novalidate"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]} + ]}, + {tag: 'frame', attr: []}, + {tag: 'frameset', attr: []}, + {tag: 'h1', attr: []}, + {tag: 'h2', attr: []}, + {tag: 'h3', attr: []}, + {tag: 'h4', attr: []}, + {tag: 'h5', attr: []}, + {tag: 'h6', attr: []}, + {tag: 'head', attr: []}, + {tag: 'header', attr: []}, + {tag: 'hgroup', attr: []}, + {tag: 'hr', attr: []}, + {tag: 'html', attr: [ + {key: 'manifest', values: []} + ]}, + {tag: 'i', attr: []}, + {tag: 'iframe', attr: [ + {key: 'src', values: []}, + {key: 'srcdoc', values: []}, + {key: 'name', values: []}, + {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]}, + {key: 'seamless', values: ["","seamless"]}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'img', attr: [ + {key: 'alt', values: []}, + {key: 'src', values: []}, + {key: 'crossorigin', values: ["anonymous","use-credentials"]}, + {key: 'ismap', values: []}, + {key: 'usemap', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'input', attr: [ + {key: 'accept', values: ["audio/*","video/*","image/*"]}, + {key: 'alt', values: []}, + {key: 'autocomplete', values: ["on","off"]}, + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'checked', values: ["","checked"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'dirname', values: []}, + {key: 'form', values: []}, + {key: 'formaction', values: []}, + {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'formmethod', values: ["get","post","put","delete"]}, + {key: 'formnovalidate', values: ["","novalidate"]}, + {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, + {key: 'height', values: []}, + {key: 'list', values: []}, + {key: 'max', values: []}, + {key: 'maxlength', values: []}, + {key: 'min', values: []}, + {key: 'multiple', values: ["","multiple"]}, + {key: 'name', values: []}, + {key: 'pattern', values: []}, + {key: 'placeholder', values: []}, + {key: 'readonly', values: ["","readonly"]}, + {key: 'required', values: ["","required"]}, + {key: 'size', values: []}, + {key: 'src', values: []}, + {key: 'step', values: []}, + {key: 'type', values: [ + "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local", + "number","range","color","checkbox","radio","file","submit","image","reset","button" + ]}, + {key: 'value', values: []}, + {key: 'width', values: []} + ]}, + {tag: 'ins', attr: [ + {key: 'cite', values: []}, + {key: 'datetime', values: []} + ]}, + {tag: 'kbd', attr: []}, + {tag: 'keygen', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'challenge', values: []}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'keytype', values: ["RSA"]}, + {key: 'name', values: []} + ]}, + {tag: 'label', attr: [ + {key: 'for', values: []}, + {key: 'form', values: []} + ]}, + {tag: 'legend', attr: []}, + {tag: 'li', attr: [ + {key: 'value', values: []} + ]}, + {tag: 'link', attr: [ + {key: 'href', values: []}, + {key: 'hreflang', values: ["en","es"]}, + {key: 'media', values: [ + "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses", + "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3", + "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait", + "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2" + ]}, + {key: 'type', values: []}, + {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]} + ]}, + {tag: 'map', attr: [ + {key: 'name', values: []} + ]}, + {tag: 'mark', attr: []}, + {tag: 'menu', attr: [ + {key: 'type', values: ["list","context","toolbar"]}, + {key: 'label', values: []} + ]}, + {tag: 'meta', attr: [ + {key: 'charset', attr: ["utf-8"]}, + {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]}, + {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]}, + {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]} + ]}, + {tag: 'meter', attr: [ + {key: 'value', values: []}, + {key: 'min', values: []}, + {key: 'low', values: []}, + {key: 'high', values: []}, + {key: 'max', values: []}, + {key: 'optimum', values: []} + ]}, + {tag: 'nav', attr: []}, + {tag: 'noframes', attr: []}, + {tag: 'noscript', attr: []}, + {tag: 'object', attr: [ + {key: 'data', values: []}, + {key: 'type', values: []}, + {key: 'typemustmatch', values: ["","typemustmatch"]}, + {key: 'name', values: []}, + {key: 'usemap', values: []}, + {key: 'form', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'ol', attr: [ + {key: 'reversed', values: ["", "reversed"]}, + {key: 'start', values: []}, + {key: 'type', values: ["1","a","A","i","I"]} + ]}, + {tag: 'optgroup', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'label', values: []} + ]}, + {tag: 'option', attr: [ + {key: 'disabled', values: ["", "disabled"]}, + {key: 'label', values: []}, + {key: 'selected', values: ["", "selected"]}, + {key: 'value', values: []} + ]}, + {tag: 'output', attr: [ + {key: 'for', values: []}, + {key: 'form', values: []}, + {key: 'name', values: []} + ]}, + {tag: 'p', attr: []}, + {tag: 'param', attr: [ + {key: 'name', values: []}, + {key: 'value', values: []} + ]}, + {tag: 'pre', attr: []}, + {tag: 'progress', attr: [ + {key: 'value', values: []}, + {key: 'max', values: []} + ]}, + {tag: 'q', attr: [ + {key: 'cite', values: []} + ]}, + {tag: 'rp', attr: []}, + {tag: 'rt', attr: []}, + {tag: 'ruby', attr: []}, + {tag: 's', attr: []}, + {tag: 'samp', attr: []}, + {tag: 'script', attr: [ + {key: 'type', values: ["text/javascript"]}, + {key: 'src', values: []}, + {key: 'async', values: ["","async"]}, + {key: 'defer', values: ["","defer"]}, + {key: 'charset', values: ["utf-8"]} + ]}, + {tag: 'section', attr: []}, + {tag: 'select', attr: [ + {key: 'autofocus', values: ["", "autofocus"]}, + {key: 'disabled', values: ["", "disabled"]}, + {key: 'form', values: []}, + {key: 'multiple', values: ["", "multiple"]}, + {key: 'name', values: []}, + {key: 'size', values: []} + ]}, + {tag: 'small', attr: []}, + {tag: 'source', attr: [ + {key: 'src', values: []}, + {key: 'type', values: []}, + {key: 'media', values: []} + ]}, + {tag: 'span', attr: []}, + {tag: 'strike', attr: []}, + {tag: 'strong', attr: []}, + {tag: 'style', attr: [ + {key: 'type', values: ["text/css"]}, + {key: 'media', values: ["all","braille","print","projection","screen","speech"]}, + {key: 'scoped', values: []} + ]}, + {tag: 'sub', attr: []}, + {tag: 'summary', attr: []}, + {tag: 'sup', attr: []}, + {tag: 'table', attr: [ + {key: 'border', values: []} + ]}, + {tag: 'tbody', attr: []}, + {tag: 'td', attr: [ + {key: 'colspan', values: []}, + {key: 'rowspan', values: []}, + {key: 'headers', values: []} + ]}, + {tag: 'textarea', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'dirname', values: []}, + {key: 'form', values: []}, + {key: 'maxlength', values: []}, + {key: 'name', values: []}, + {key: 'placeholder', values: []}, + {key: 'readonly', values: ["","readonly"]}, + {key: 'required', values: ["","required"]}, + {key: 'rows', values: []}, + {key: 'cols', values: []}, + {key: 'wrap', values: ["soft","hard"]} + ]}, + {tag: 'tfoot', attr: []}, + {tag: 'th', attr: [ + {key: 'colspan', values: []}, + {key: 'rowspan', values: []}, + {key: 'headers', values: []}, + {key: 'scope', values: ["row","col","rowgroup","colgroup"]} + ]}, + {tag: 'thead', attr: []}, + {tag: 'time', attr: [ + {key: 'datetime', values: []} + ]}, + {tag: 'title', attr: []}, + {tag: 'tr', attr: []}, + {tag: 'track', attr: [ + {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]}, + {key: 'src', values: []}, + {key: 'srclang', values: ["en","es"]}, + {key: 'label', values: []}, + {key: 'default', values: []} + ]}, + {tag: 'tt', attr: []}, + {tag: 'u', attr: []}, + {tag: 'ul', attr: []}, + {tag: 'var', attr: []}, + {tag: 'video', attr: [ + {key: "src", values: []}, + {key: "crossorigin", values: ["anonymous","use-credentials"]}, + {key: "poster", values: []}, + {key: "preload", values: ["auto","metadata","none"]}, + {key: "autoplay", values: ["","autoplay"]}, + {key: "mediagroup", values: ["movie"]}, + {key: "loop", values: ["","loop"]}, + {key: "muted", values: ["","muted"]}, + {key: "controls", values: ["","controls"]}, + {key: "width", values: []}, + {key: "height", values: []} + ]}, + {tag: 'wbr', attr: []} + ]; + + var globalAttributes = [ + {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, + {key: "class", values: []}, + {key: "contenteditable", values: ["true", "false"]}, + {key: "contextmenu", values: []}, + {key: "dir", values: ["ltr","rtl","auto"]}, + {key: "draggable", values: ["true","false","auto"]}, + {key: "dropzone", values: ["copy","move","link","string:","file:"]}, + {key: "hidden", values: ["hidden"]}, + {key: "id", values: []}, + {key: "inert", values: ["inert"]}, + {key: "itemid", values: []}, + {key: "itemprop", values: []}, + {key: "itemref", values: []}, + {key: "itemscope", values: ["itemscope"]}, + {key: "itemtype", values: []}, + {key: "lang", values: ["en","es"]}, + {key: "spellcheck", values: ["true","false"]}, + {key: "style", values: []}, + {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]}, + {key: "title", values: []}, + {key: "translate", values: ["yes","no"]}, + {key: "onclick", values: []}, + {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} + ]; + + CodeMirror.htmlHint = function(editor) { + if(String.prototype.trim == undefined) { + String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; + } + return htmlHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); + }; +})(); diff --git a/gulliver/js/codemirror/addon/hint/javascript-hint.js b/gulliver/js/codemirror/addon/hint/javascript-hint.js new file mode 100644 index 000000000..fe2240af1 --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/javascript-hint.js @@ -0,0 +1,140 @@ +(function () { + var Pos = CodeMirror.Pos; + + function forEach(arr, f) { + for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); + } + + function arrayContains(arr, item) { + if (!Array.prototype.indexOf) { + var i = arr.length; + while (i--) { + if (arr[i] === item) { + return true; + } + } + return false; + } + return arr.indexOf(item) != -1; + } + + function scriptHint(editor, keywords, getToken, options) { + // Find the token at the cursor + var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; + // If it's not a 'word-style' token, ignore the token. + if (!/^[\w$_]*$/.test(token.string)) { + token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, + type: token.string == "." ? "property" : null}; + } + // If it is a property, find out what it is a property of. + while (tprop.type == "property") { + tprop = getToken(editor, Pos(cur.line, tprop.start)); + if (tprop.string != ".") return; + tprop = getToken(editor, Pos(cur.line, tprop.start)); + if (tprop.string == ')') { + var level = 1; + do { + tprop = getToken(editor, Pos(cur.line, tprop.start)); + switch (tprop.string) { + case ')': level++; break; + case '(': level--; break; + default: break; + } + } while (level > 0); + tprop = getToken(editor, Pos(cur.line, tprop.start)); + if (tprop.type.indexOf("variable") === 0) + tprop.type = "function"; + else return; // no clue + } + if (!context) var context = []; + context.push(tprop); + } + return {list: getCompletions(token, context, keywords, options), + from: Pos(cur.line, token.start), + to: Pos(cur.line, token.end)}; + } + + CodeMirror.javascriptHint = function(editor, options) { + return scriptHint(editor, javascriptKeywords, + function (e, cur) {return e.getTokenAt(cur);}, + options); + }; + + function getCoffeeScriptToken(editor, cur) { + // This getToken, it is for coffeescript, imitates the behavior of + // getTokenAt method in javascript.js, that is, returning "property" + // type and treat "." as indepenent token. + var token = editor.getTokenAt(cur); + if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') { + token.end = token.start; + token.string = '.'; + token.type = "property"; + } + else if (/^\.[\w$_]*$/.test(token.string)) { + token.type = "property"; + token.start++; + token.string = token.string.replace(/\./, ''); + } + return token; + } + + CodeMirror.coffeescriptHint = function(editor, options) { + return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); + }; + + var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + + "toUpperCase toLowerCase split concat match replace search").split(" "); + var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " + + "lastIndexOf every some filter forEach map reduce reduceRight ").split(" "); + var funcProps = "prototype apply call bind".split(" "); + var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " + + "if in instanceof new null return switch throw true try typeof var void while with").split(" "); + var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " + + "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" "); + + function getCompletions(token, context, keywords, options) { + var found = [], start = token.string; + function maybeAdd(str) { + if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + } + function gatherCompletions(obj) { + if (typeof obj == "string") forEach(stringProps, maybeAdd); + else if (obj instanceof Array) forEach(arrayProps, maybeAdd); + else if (obj instanceof Function) forEach(funcProps, maybeAdd); + for (var name in obj) maybeAdd(name); + } + + if (context) { + // If this is a property, see if it belongs to some object we can + // find in the current environment. + var obj = context.pop(), base; + if (obj.type.indexOf("variable") === 0) { + if (options && options.additionalContext) + base = options.additionalContext[obj.string]; + base = base || window[obj.string]; + } else if (obj.type == "string") { + base = ""; + } else if (obj.type == "atom") { + base = 1; + } else if (obj.type == "function") { + if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && + (typeof window.jQuery == 'function')) + base = window.jQuery(); + else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function')) + base = window._(); + } + while (base != null && context.length) + base = base[context.pop().string]; + if (base != null) gatherCompletions(base); + } + else { + // If not, just look in the window object and any local scope + // (reading into JS mode internals to get at the local and global variables) + for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); + for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name); + gatherCompletions(window); + forEach(keywords, maybeAdd); + } + return found; + } +})(); diff --git a/gulliver/js/codemirror/addon/hint/pig-hint.js b/gulliver/js/codemirror/addon/hint/pig-hint.js new file mode 100644 index 000000000..9847996be --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/pig-hint.js @@ -0,0 +1,117 @@ +(function () { + function forEach(arr, f) { + for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); + } + + function arrayContains(arr, item) { + if (!Array.prototype.indexOf) { + var i = arr.length; + while (i--) { + if (arr[i] === item) { + return true; + } + } + return false; + } + return arr.indexOf(item) != -1; + } + + function scriptHint(editor, _keywords, getToken) { + // Find the token at the cursor + var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; + // If it's not a 'word-style' token, ignore the token. + + if (!/^[\w$_]*$/.test(token.string)) { + token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, + className: token.string == ":" ? "pig-type" : null}; + } + + if (!context) var context = []; + context.push(tprop); + + var completionList = getCompletions(token, context); + completionList = completionList.sort(); + //prevent autocomplete for last word, instead show dropdown with one word + if(completionList.length == 1) { + completionList.push(" "); + } + + return {list: completionList, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end)}; + } + + CodeMirror.pigHint = function(editor) { + return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); + }; + + 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 " + + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " + + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + + "NEQ MATCHES TRUE FALSE"; + var pigKeywordsU = pigKeywords.split(" "); + var pigKeywordsL = pigKeywords.toLowerCase().split(" "); + + var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP"; + var pigTypesU = pigTypes.split(" "); + var pigTypesL = pigTypes.toLowerCase().split(" "); + + var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " + + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " + + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " + + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER " + + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS " + + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " + + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " + + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " + + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; + var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); + var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); + var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs " + + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax " + + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum " + + "InvokeForDouble InvokeForFloat InvokeForInt InvokeForLong InvokeForString Invoker " + + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize " + + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax " + + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" "); + + function getCompletions(token, context) { + var found = [], start = token.string; + function maybeAdd(str) { + if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + } + + function gatherCompletions(obj) { + if(obj == ":") { + forEach(pigTypesL, maybeAdd); + } + else { + forEach(pigBuiltinsU, maybeAdd); + forEach(pigBuiltinsL, maybeAdd); + forEach(pigBuiltinsC, maybeAdd); + forEach(pigTypesU, maybeAdd); + forEach(pigTypesL, maybeAdd); + forEach(pigKeywordsU, maybeAdd); + forEach(pigKeywordsL, maybeAdd); + } + } + + if (context) { + // If this is a property, see if it belongs to some object we can + // find in the current environment. + var obj = context.pop(), base; + + if (obj.type == "variable") + base = obj.string; + else if(obj.type == "variable-3") + base = ":" + obj.string; + + while (base != null && context.length) + base = base[context.pop().string]; + if (base != null) gatherCompletions(base); + } + return found; + } +})(); diff --git a/gulliver/js/codemirror/addon/hint/python-hint.js b/gulliver/js/codemirror/addon/hint/python-hint.js new file mode 100644 index 000000000..60221b89e --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/python-hint.js @@ -0,0 +1,93 @@ +(function () { + function forEach(arr, f) { + for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); + } + + function arrayContains(arr, item) { + if (!Array.prototype.indexOf) { + var i = arr.length; + while (i--) { + if (arr[i] === item) { + return true; + } + } + return false; + } + return arr.indexOf(item) != -1; + } + + function scriptHint(editor, _keywords, getToken) { + // Find the token at the cursor + var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; + // If it's not a 'word-style' token, ignore the token. + + if (!/^[\w$_]*$/.test(token.string)) { + token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, + className: token.string == ":" ? "python-type" : null}; + } + + if (!context) var context = []; + context.push(tprop); + + var completionList = getCompletions(token, context); + completionList = completionList.sort(); + //prevent autocomplete for last word, instead show dropdown with one word + if(completionList.length == 1) { + completionList.push(" "); + } + + return {list: completionList, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end)}; + } + + CodeMirror.pythonHint = function(editor) { + return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); + }; + + 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"; + var pythonKeywordsL = pythonKeywords.split(" "); + var pythonKeywordsU = pythonKeywords.toUpperCase().split(" "); + + var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str " ++ "any eval isinstance pow sum basestring execfile issubclass print super" ++ "bin file iter property tuple bool filter len range type" ++ "bytearray float list raw_input unichr callable format locals reduce unicode" ++ "chr frozenset long reload vars classmethod getattr map repr xrange" ++ "cmp globals max reversed zip compile hasattr memoryview round __import__" ++ "complex hash min set apply delattr help next setattr buffer" ++ "dict hex object slice coerce dir id oct sorted intern "; + var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" "); + var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" "); + + function getCompletions(token, context) { + var found = [], start = token.string; + function maybeAdd(str) { + if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + } + + function gatherCompletions(_obj) { + forEach(pythonBuiltinsL, maybeAdd); + forEach(pythonBuiltinsU, maybeAdd); + forEach(pythonKeywordsL, maybeAdd); + forEach(pythonKeywordsU, maybeAdd); + } + + if (context) { + // If this is a property, see if it belongs to some object we can + // find in the current environment. + var obj = context.pop(), base; + + if (obj.type == "variable") + base = obj.string; + else if(obj.type == "variable-3") + base = ":" + obj.string; + + while (base != null && context.length) + base = base[context.pop().string]; + if (base != null) gatherCompletions(base); + } + return found; + } +})(); diff --git a/gulliver/js/codemirror/addon/hint/xml-hint.js b/gulliver/js/codemirror/addon/hint/xml-hint.js new file mode 100644 index 000000000..42eab6f3b --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/xml-hint.js @@ -0,0 +1,118 @@ +(function() { + + CodeMirror.xmlHints = []; + + CodeMirror.xmlHint = function(cm) { + + var cursor = cm.getCursor(); + + if (cursor.ch > 0) { + + var text = cm.getRange(CodeMirror.Pos(0, 0), cursor); + var typed = ''; + var simbol = ''; + for(var i = text.length - 1; i >= 0; i--) { + if(text[i] == ' ' || text[i] == '<') { + simbol = text[i]; + break; + } + else { + typed = text[i] + typed; + } + } + + text = text.slice(0, text.length - typed.length); + + var path = getActiveElement(text) + simbol; + var hints = CodeMirror.xmlHints[path]; + + if(typeof hints === 'undefined') + hints = ['']; + else { + hints = hints.slice(0); + for (var i = hints.length - 1; i >= 0; i--) { + if(hints[i].indexOf(typed) != 0) + hints.splice(i, 1); + } + } + + return { + list: hints, + from: CodeMirror.Pos(cursor.line, cursor.ch - typed.length), + to: cursor + }; + } + }; + + var getActiveElement = function(text) { + + var element = ''; + + if(text.length >= 0) { + + var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g'); + + var matches = []; + var match; + while ((match = regex.exec(text)) != null) { + matches.push({ + tag: match[1], + selfclose: (match[0].slice(match[0].length - 2) === '/>') + }); + } + + for (var i = matches.length - 1, skip = 0; i >= 0; i--) { + + var item = matches[i]; + + if (item.tag[0] == '/') + { + skip++; + } + else if (item.selfclose == false) + { + if (skip > 0) + { + skip--; + } + else + { + element = '<' + item.tag + '>' + element; + } + } + } + + element += getOpenTag(text); + } + + return element; + }; + + var getOpenTag = function(text) { + + var open = text.lastIndexOf('<'); + var close = text.lastIndexOf('>'); + + if (close < open) + { + text = text.slice(open); + + if(text != '<') { + + var space = text.indexOf(' '); + if(space < 0) + space = text.indexOf('\t'); + if(space < 0) + space = text.indexOf('\n'); + + if (space < 0) + space = text.length; + + return text.slice(0, space); + } + } + + return ''; + }; + +})(); diff --git a/gulliver/js/codemirror/addon/lint/javascript-lint.js b/gulliver/js/codemirror/addon/lint/javascript-lint.js new file mode 100644 index 000000000..db9ff6b22 --- /dev/null +++ b/gulliver/js/codemirror/addon/lint/javascript-lint.js @@ -0,0 +1,127 @@ +(function() { + + var bogus = [ "Dangerous comment" ]; + + var warnings = [ [ "Expected '{'", + "Statement body should be inside '{ }' braces." ] ]; + + var errors = [ "Missing semicolon", "Extra comma", "Missing property name", + "Unmatched ", " and instead saw", " is not defined", + "Unclosed string", "Stopping, unable to continue" ]; + + function validator(options, text) { + 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); + + function cleanup(error) { + // All problems are warnings by default + fixWith(error, warnings, "warning", true); + fixWith(error, errors, "error"); + + return isBogus(error) ? null : error; + } + + function fixWith(error, fixes, severity, force) { + var description, fix, find, replace, found; + + description = error.description; + + for ( var i = 0; i < fixes.length; i++) { + fix = fixes[i]; + find = (typeof fix === "string" ? fix : fix[0]); + replace = (typeof fix === "string" ? null : fix[1]); + found = description.indexOf(find) !== -1; + + if (force || found) { + error.severity = severity; + } + if (found && replace) { + error.description = replace; + } + } + } + + function isBogus(error) { + var description = error.description; + for ( var i = 0; i < bogus.length; i++) { + if (description.indexOf(bogus[i]) !== -1) { + return true; + } + } + return false; + } + + function parseErrors(errors, output) { + for ( var i = 0; i < errors.length; i++) { + var error = errors[i]; + if (error) { + var linetabpositions, index; + + linetabpositions = []; + + // This next block is to fix a problem in jshint. Jshint + // replaces + // all tabs with spaces then performs some checks. The error + // positions (character/space) are then reported incorrectly, + // not taking the replacement step into account. Here we look + // at the evidence line and try to adjust the character position + // to the correct value. + if (error.evidence) { + // Tab positions are computed once per line and cached + var tabpositions = linetabpositions[error.line]; + if (!tabpositions) { + var evidence = error.evidence; + tabpositions = []; + // ugggh phantomjs does not like this + // forEachChar(evidence, function(item, index) { + Array.prototype.forEach.call(evidence, function(item, + index) { + if (item === '\t') { + // First col is 1 (not 0) to match error + // positions + tabpositions.push(index + 1); + } + }); + linetabpositions[error.line] = tabpositions; + } + if (tabpositions.length > 0) { + var pos = error.character; + tabpositions.forEach(function(tabposition) { + if (pos > tabposition) pos -= 1; + }); + error.character = pos; + } + } + + var start = error.character - 1, end = start + 1; + if (error.evidence) { + index = error.evidence.substring(start).search(/.\b/); + if (index > -1) { + end += index; + } + } + + // Convert to format expected by validation service + error.description = error.reason;// + "(jshint)"; + error.start = error.character; + error.end = end; + error = cleanup(error); + + if (error) + output.push({message: error.description, + severity: error.severity, + from: CodeMirror.Pos(error.line - 1, start), + to: CodeMirror.Pos(error.line - 1, end)}); + } + } + } +})(); diff --git a/gulliver/js/codemirror/addon/lint/json-lint.js b/gulliver/js/codemirror/addon/lint/json-lint.js new file mode 100644 index 000000000..42b36abb3 --- /dev/null +++ b/gulliver/js/codemirror/addon/lint/json-lint.js @@ -0,0 +1,14 @@ +// Depends on jsonlint.js from https://github.com/zaach/jsonlint + +CodeMirror.jsonValidator = function(text) { + var found = []; + jsonlint.parseError = function(str, hash) { + var loc = hash.loc; + found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), + to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), + message: str}); + }; + try { jsonlint.parse(text); } + catch(e) {} + return found; +}; diff --git a/gulliver/js/codemirror/addon/lint/lint.css b/gulliver/js/codemirror/addon/lint/lint.css new file mode 100644 index 000000000..4fd72e774 --- /dev/null +++ b/gulliver/js/codemirror/addon/lint/lint.css @@ -0,0 +1,96 @@ +/* The lint marker gutter */ +.CodeMirror-lint-markers { + width: 16px; +} + +.CodeMirror-lint-tooltip { + background-color: infobackground; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: infotext; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px 5px; + position: fixed; + white-space: pre; + z-index: 100; + max-width: 600px; + opacity: 0; + transition: opacity .4s; + -moz-transition: opacity .4s; + -webkit-transition: opacity .4s; + -o-transition: opacity .4s; + -ms-transition: opacity .4s; +} + +.CodeMirror-lint-span-error, .CodeMirror-lint-span-warning { + background-position: left bottom; + background-repeat: repeat-x; +} + +.CodeMirror-lint-span-error { + background-image: + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") + ; +} + +.CodeMirror-lint-span-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); +} + +.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; + display: inline-block; + height: 16px; + width: 16px; + vertical-align: middle; + position: relative; +} + +.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { + padding-left: 18px; + background-position: top left; + background-repeat: no-repeat; +} + +.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="); +} + +.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=="); +} + +.CodeMirror-lint-marker-multiple { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + 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/gulliver/js/codemirror/addon/lint/lint.js b/gulliver/js/codemirror/addon/lint/lint.js new file mode 100644 index 000000000..dd838e7ea --- /dev/null +++ b/gulliver/js/codemirror/addon/lint/lint.js @@ -0,0 +1,183 @@ +CodeMirror.validate = (function() { + var GUTTER_ID = "CodeMirror-lint-markers"; + var SEVERITIES = /^(?:error|warning)$/; + + function showTooltip(e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip"; + tt.appendChild(content.cloneNode(true)); + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = (e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function LintState(cm, options, hasGutter) { + this.marked = []; + this.options = options; + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + } + + function parseOptions(options) { + if (options instanceof Function) return {getAnnotations: options}; + else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); + return options; + } + + function clearMarks(cm) { + var state = cm._lintState; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function makeMarker(labels, severity, multiple) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker-multiple"; + } + + var tooltip; + CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); }); + CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!SEVERITIES.test(severity)) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message-" + severity; + tip.appendChild(document.createTextNode(ann.message)); + return tip; + } + + function startLinting(cm) { + var state = cm._lintState, options = state.options; + if (options.async) + options.getAnnotations(cm, updateLinting, options); + else + updateLinting(cm, options.getAnnotations(cm.getValue())); + } + + function updateLinting(cm, annotationsNotSorted) { + clearMarks(cm); + var state = cm._lintState, options = state.options; + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!SEVERITIES.test(severity)) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-span-" + severity, + __annotation: ann + })); + } + + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1)); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm._lintState; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); + } + + function popupSpanTooltip(ann, e) { + var tooltip = showTooltip(e, annotationTooltip(ann)); + var target = e.target || e.srcElement; + CodeMirror.on(target, "mouseout", hide); + function hide() { + CodeMirror.off(target, "mouseout", hide); + hideTooltip(tooltip); + } + } + + // When the mouseover fires, the cursor might not actually be over + // the character itself yet. These pairs of x,y offsets are used to + // probe a few nearby points when no suitable marked range is found. + var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0]; + + function onMouseOver(cm, e) { + if (!/\bCodeMirror-lint-span-/.test((e.target || e.srcElement).className)) return; + for (var i = 0; i < nearby.length; i += 2) { + var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i], + top: e.clientY + nearby[i + 1]})); + for (var j = 0; j < spans.length; ++j) { + var span = spans[j], ann = span.__annotation; + if (ann) return popupSpanTooltip(ann, e); + } + } + } + + CodeMirror.defineOption("lintWith", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver); + delete cm._lintState; + } + + 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._lintState = new LintState(cm, parseOptions(val), hasLintGutter); + cm.on("change", onChange); + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + startLinting(cm); + } + }); +})(); diff --git a/gulliver/js/codemirror/addon/mode/loadmode.js b/gulliver/js/codemirror/addon/mode/loadmode.js new file mode 100644 index 000000000..60fafbb17 --- /dev/null +++ b/gulliver/js/codemirror/addon/mode/loadmode.js @@ -0,0 +1,51 @@ +(function() { + if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; + + var loading = {}; + function splitCallback(cont, n) { + var countDown = n; + return function() { if (--countDown == 0) cont(); }; + } + function ensureDeps(mode, cont) { + var deps = CodeMirror.modes[mode].dependencies; + if (!deps) return cont(); + var missing = []; + for (var i = 0; i < deps.length; ++i) { + if (!CodeMirror.modes.hasOwnProperty(deps[i])) + missing.push(deps[i]); + } + if (!missing.length) return cont(); + var split = splitCallback(cont, missing.length); + for (var i = 0; i < missing.length; ++i) + CodeMirror.requireMode(missing[i], split); + } + + CodeMirror.requireMode = function(mode, cont) { + if (typeof mode != "string") mode = mode.name; + if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); + if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); + + var script = document.createElement("script"); + script.src = CodeMirror.modeURL.replace(/%N/g, mode); + var others = document.getElementsByTagName("script")[0]; + others.parentNode.insertBefore(script, others); + var list = loading[mode] = [cont]; + var count = 0, poll = setInterval(function() { + if (++count > 100) return clearInterval(poll); + if (CodeMirror.modes.hasOwnProperty(mode)) { + clearInterval(poll); + loading[mode] = null; + ensureDeps(mode, function() { + for (var i = 0; i < list.length; ++i) list[i](); + }); + } + }, 200); + }; + + CodeMirror.autoLoadMode = function(instance, mode) { + if (!CodeMirror.modes.hasOwnProperty(mode)) + CodeMirror.requireMode(mode, function() { + instance.setOption("mode", instance.getOption("mode")); + }); + }; +}()); diff --git a/gulliver/js/codemirror/addon/mode/multiplex.js b/gulliver/js/codemirror/addon/mode/multiplex.js new file mode 100644 index 000000000..e77ff2a9c --- /dev/null +++ b/gulliver/js/codemirror/addon/mode/multiplex.js @@ -0,0 +1,95 @@ +CodeMirror.multiplexingMode = function(outer /*, others */) { + // Others should be {open, close, mode [, delimStyle]} objects + var others = Array.prototype.slice.call(arguments, 1); + var n_others = others.length; + + function indexOf(string, pattern, from) { + if (typeof pattern == "string") return string.indexOf(pattern, from); + var m = pattern.exec(from ? string.slice(from) : string); + return m ? m.index + from : -1; + } + + return { + startState: function() { + return { + outer: CodeMirror.startState(outer), + innerActive: null, + inner: null + }; + }, + + copyState: function(state) { + return { + outer: CodeMirror.copyState(outer, state.outer), + innerActive: state.innerActive, + inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner) + }; + }, + + token: function(stream, state) { + if (!state.innerActive) { + var cutOff = Infinity, oldContent = stream.string; + for (var i = 0; i < n_others; ++i) { + var other = others[i]; + var found = indexOf(oldContent, other.open, stream.pos); + if (found == stream.pos) { + stream.match(other.open); + state.innerActive = other; + state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0); + return other.delimStyle; + } else if (found != -1 && found < cutOff) { + cutOff = found; + } + } + if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff); + var outerToken = outer.token(stream, state.outer); + if (cutOff != Infinity) stream.string = oldContent; + return outerToken; + } else { + var curInner = state.innerActive, oldContent = stream.string; + var found = indexOf(oldContent, curInner.close, stream.pos); + if (found == stream.pos) { + stream.match(curInner.close); + state.innerActive = state.inner = null; + return curInner.delimStyle; + } + if (found > -1) stream.string = oldContent.slice(0, found); + var innerToken = curInner.mode.token(stream, state.inner); + if (found > -1) stream.string = oldContent; + var cur = stream.current(), found = cur.indexOf(curInner.close); + if (found > -1) stream.backUp(cur.length - found); + return innerToken; + } + }, + + indent: function(state, textAfter) { + var mode = state.innerActive ? state.innerActive.mode : outer; + if (!mode.indent) return CodeMirror.Pass; + return mode.indent(state.innerActive ? state.inner : state.outer, textAfter); + }, + + blankLine: function(state) { + var mode = state.innerActive ? state.innerActive.mode : outer; + if (mode.blankLine) { + mode.blankLine(state.innerActive ? state.inner : state.outer); + } + if (!state.innerActive) { + for (var i = 0; i < n_others; ++i) { + var other = others[i]; + if (other.open === "\n") { + state.innerActive = other; + state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0); + } + } + } else if (mode.close === "\n") { + state.innerActive = state.inner = null; + } + }, + + electricChars: outer.electricChars, + + innerMode: function(state) { + return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer}; + } + }; +}; diff --git a/gulliver/js/codemirror/addon/mode/overlay.js b/gulliver/js/codemirror/addon/mode/overlay.js new file mode 100644 index 000000000..fba38987b --- /dev/null +++ b/gulliver/js/codemirror/addon/mode/overlay.js @@ -0,0 +1,59 @@ +// Utility function that allows modes to be combined. The mode given +// as the base argument takes care of most of the normal mode +// functionality, but a second (typically simple) mode is used, which +// can override the style of text. Both modes get to parse all of the +// text, but when both assign a non-null style to a piece of code, the +// overlay wins, unless the combine argument was true, in which case +// the styles are combined. + +// overlayParser is the old, deprecated name +CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) { + return { + startState: function() { + return { + base: CodeMirror.startState(base), + overlay: CodeMirror.startState(overlay), + basePos: 0, baseCur: null, + overlayPos: 0, overlayCur: null + }; + }, + copyState: function(state) { + return { + base: CodeMirror.copyState(base, state.base), + overlay: CodeMirror.copyState(overlay, state.overlay), + basePos: state.basePos, baseCur: null, + overlayPos: state.overlayPos, overlayCur: null + }; + }, + + token: function(stream, state) { + if (stream.start == state.basePos) { + state.baseCur = base.token(stream, state.base); + state.basePos = stream.pos; + } + if (stream.start == state.overlayPos) { + stream.pos = stream.start; + state.overlayCur = overlay.token(stream, state.overlay); + state.overlayPos = stream.pos; + } + stream.pos = Math.min(state.basePos, state.overlayPos); + if (stream.eol()) state.basePos = state.overlayPos = 0; + + if (state.overlayCur == null) return state.baseCur; + if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; + else return state.overlayCur; + }, + + indent: base.indent && function(state, textAfter) { + return base.indent(state.base, textAfter); + }, + electricChars: base.electricChars, + + innerMode: function(state) { return {state: state.base, mode: base}; }, + + blankLine: function(state) { + if (base.blankLine) base.blankLine(state.base); + if (overlay.blankLine) overlay.blankLine(state.overlay); + } + }; +}; diff --git a/gulliver/js/codemirror/addon/runmode/colorize.js b/gulliver/js/codemirror/addon/runmode/colorize.js new file mode 100644 index 000000000..62286d21e --- /dev/null +++ b/gulliver/js/codemirror/addon/runmode/colorize.js @@ -0,0 +1,29 @@ +CodeMirror.colorize = (function() { + + var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; + + function textContent(node, out) { + if (node.nodeType == 3) return out.push(node.nodeValue); + for (var ch = node.firstChild; ch; ch = ch.nextSibling) { + textContent(ch, out); + if (isBlock.test(node.nodeType)) out.push("\n"); + } + } + + return function(collection, defaultMode) { + if (!collection) collection = document.body.getElementsByTagName("pre"); + + for (var i = 0; i < collection.length; ++i) { + var node = collection[i]; + var mode = node.getAttribute("data-lang") || defaultMode; + if (!mode) continue; + + var text = []; + textContent(node, text); + node.innerHTML = ""; + CodeMirror.runMode(text.join(""), mode, node); + + node.className += " cm-s-default"; + } + }; +})(); diff --git a/gulliver/js/codemirror/addon/runmode/runmode-standalone.js b/gulliver/js/codemirror/addon/runmode/runmode-standalone.js new file mode 100644 index 000000000..7a9b82ffa --- /dev/null +++ b/gulliver/js/codemirror/addon/runmode/runmode-standalone.js @@ -0,0 +1,130 @@ +/* Just enough of CodeMirror to run runMode under node.js */ + +window.CodeMirror = {}; + +function splitLines(string){ return string.split(/\r?\n|\r/); }; + +function StringStream(string) { + this.pos = this.start = 0; + this.string = string; +} +StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == 0;}, + peek: function() {return this.string.charAt(this.pos) || null;}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() {return this.start;}, + indentation: function() {return 0;}, + 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) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);} +}; +CodeMirror.StringStream = StringStream; + +CodeMirror.startState = function (mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; +}; + +var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; +CodeMirror.defineMode = function (name, mode) { modes[name] = mode; }; +CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; }; +CodeMirror.getMode = function (options, spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + spec = mimeModes[spec]; + if (typeof spec == "string") + var mname = spec, config = {}; + else if (spec != null) + var mname = spec.name, config = spec; + var mfactory = modes[mname]; + if (!mfactory) throw new Error("Unknown mode: " + spec); + return mfactory(options, config || {}); +}; + +CodeMirror.runMode = function (string, modespec, callback, options) { + var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec); + + if (callback.nodeType == 1) { + var tabSize = (options && options.tabSize) || 4; + var node = callback, col = 0; + node.innerHTML = ""; + callback = function (text, style) { + if (text == "\n") { + node.appendChild(document.createElement("br")); + col = 0; + return; + } + var content = ""; + // replace tabs + for (var pos = 0; ;) { + var idx = text.indexOf("\t", pos); + if (idx == -1) { + content += text.slice(pos); + col += text.length - pos; + break; + } else { + col += idx - pos; + content += text.slice(pos, idx); + var size = tabSize - col % tabSize; + col += size; + for (var i = 0; i < size; ++i) content += " "; + pos = idx + 1; + } + } + + if (style) { + var sp = node.appendChild(document.createElement("span")); + sp.className = "cm-" + style.replace(/ +/g, " cm-"); + sp.appendChild(document.createTextNode(content)); + } else { + node.appendChild(document.createTextNode(content)); + } + }; + } + + var lines = splitLines(string), state = CodeMirror.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) callback("\n"); + var stream = new CodeMirror.StringStream(lines[i]); + while (!stream.eol()) { + var style = mode.token(stream, state); + callback(stream.current(), style, i, stream.start); + stream.start = stream.pos; + } + } +}; diff --git a/gulliver/js/codemirror/addon/runmode/runmode.js b/gulliver/js/codemirror/addon/runmode/runmode.js new file mode 100644 index 000000000..3e1bed736 --- /dev/null +++ b/gulliver/js/codemirror/addon/runmode/runmode.js @@ -0,0 +1,52 @@ +CodeMirror.runMode = function(string, modespec, callback, options) { + var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); + + if (callback.nodeType == 1) { + var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; + var node = callback, col = 0; + node.innerHTML = ""; + callback = function(text, style) { + if (text == "\n") { + node.appendChild(document.createElement("br")); + col = 0; + return; + } + var content = ""; + // replace tabs + for (var pos = 0;;) { + var idx = text.indexOf("\t", pos); + if (idx == -1) { + content += text.slice(pos); + col += text.length - pos; + break; + } else { + col += idx - pos; + content += text.slice(pos, idx); + var size = tabSize - col % tabSize; + col += size; + for (var i = 0; i < size; ++i) content += " "; + pos = idx + 1; + } + } + + if (style) { + var sp = node.appendChild(document.createElement("span")); + sp.className = "cm-" + style.replace(/ +/g, " cm-"); + sp.appendChild(document.createTextNode(content)); + } else { + node.appendChild(document.createTextNode(content)); + } + }; + } + + var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) callback("\n"); + var stream = new CodeMirror.StringStream(lines[i]); + while (!stream.eol()) { + var style = mode.token(stream, state); + callback(stream.current(), style, i, stream.start); + stream.start = stream.pos; + } + } +}; diff --git a/gulliver/js/codemirror/addon/runmode/runmode.node.js b/gulliver/js/codemirror/addon/runmode/runmode.node.js new file mode 100644 index 000000000..6449e77c8 --- /dev/null +++ b/gulliver/js/codemirror/addon/runmode/runmode.node.js @@ -0,0 +1,89 @@ +/* Just enough of CodeMirror to run runMode under node.js */ + +function splitLines(string){ return string.split(/\r?\n|\r/); }; + +function StringStream(string) { + this.pos = this.start = 0; + this.string = string; +} +StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == 0;}, + peek: function() {return this.string.charAt(this.pos) || null;}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() {return this.start;}, + indentation: function() {return 0;}, + 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) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);} +}; +exports.StringStream = StringStream; + +exports.startState = function(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; +}; + +var modes = exports.modes = {}, mimeModes = exports.mimeModes = {}; +exports.defineMode = function(name, mode) { modes[name] = mode; }; +exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; +exports.getMode = function(options, spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + spec = mimeModes[spec]; + if (typeof spec == "string") + var mname = spec, config = {}; + else if (spec != null) + var mname = spec.name, config = spec; + var mfactory = modes[mname]; + if (!mfactory) throw new Error("Unknown mode: " + spec); + return mfactory(options, config || {}); +}; + +exports.runMode = function(string, modespec, callback) { + var mode = exports.getMode({indentUnit: 2}, modespec); + var lines = splitLines(string), state = exports.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) callback("\n"); + var stream = new exports.StringStream(lines[i]); + while (!stream.eol()) { + var style = mode.token(stream, state); + callback(stream.current(), style, i, stream.start); + stream.start = stream.pos; + } + } +}; diff --git a/gulliver/js/codemirror/addon/search/match-highlighter.js b/gulliver/js/codemirror/addon/search/match-highlighter.js new file mode 100644 index 000000000..c6e35cd97 --- /dev/null +++ b/gulliver/js/codemirror/addon/search/match-highlighter.js @@ -0,0 +1,60 @@ +// Highlighting text that matches the selection +// +// Defines an option highlightSelectionMatches, which, when enabled, +// will style strings that match the selection throughout the +// document. +// +// The option can be set to true to simply enable it, or to a +// {minChars, style} object to explicitly configure it. minChars is +// the minimum amount of characters that should be selected for the +// behavior to occur, and style is the token style to apply to the +// matches. This will be prefixed by "cm-" to create an actual CSS +// class name. + +(function() { + var DEFAULT_MIN_CHARS = 2; + var DEFAULT_TOKEN_STYLE = "matchhighlight"; + + function State(options) { + this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS; + this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE; + this.overlay = null; + } + + CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + cm._matchHighlightState = new State(val); + cm.on("cursorActivity", highlightMatches); + } else if (!val && prev) { + var over = cm._matchHighlightState.overlay; + if (over) cm.removeOverlay(over); + cm._matchHighlightState = null; + cm.off("cursorActivity", highlightMatches); + } + }); + + function highlightMatches(cm) { + cm.operation(function() { + var state = cm._matchHighlightState; + if (state.overlay) { + cm.removeOverlay(state.overlay); + state.overlay = null; + } + + if (!cm.somethingSelected()) return; + var selection = cm.getSelection().replace(/^\s+|\s+$/g, ""); + if (selection.length < state.minChars) return; + + cm.addOverlay(state.overlay = makeOverlay(selection, state.style)); + }); + } + + function makeOverlay(query, style) { + return {token: function(stream) { + if (stream.match(query)) return style; + stream.next(); + stream.skipTo(query.charAt(0)) || stream.skipToEnd(); + }}; + } +})(); diff --git a/gulliver/js/codemirror/addon/search/search.js b/gulliver/js/codemirror/addon/search/search.js new file mode 100644 index 000000000..6331b8655 --- /dev/null +++ b/gulliver/js/codemirror/addon/search/search.js @@ -0,0 +1,131 @@ +// Define search commands. Depends on dialog.js or another +// implementation of the openDialog method. + +// Replace works a little oddly -- it will do the replace on the next +// Ctrl-G (or whatever is bound to findNext) press. You prevent a +// replace by making sure the match is no longer selected when hitting +// Ctrl-G. + +(function() { + function searchOverlay(query) { + if (typeof query == "string") return {token: function(stream) { + if (stream.match(query)) return "searching"; + stream.next(); + stream.skipTo(query.charAt(0)) || stream.skipToEnd(); + }}; + return {token: function(stream) { + if (stream.match(query)) return "searching"; + while (!stream.eol()) { + stream.next(); + if (stream.match(query, false)) break; + } + }}; + } + + function SearchState() { + this.posFrom = this.posTo = this.query = null; + this.overlay = null; + } + function getSearchState(cm) { + return cm._searchState || (cm._searchState = new SearchState()); + } + function getSearchCursor(cm, query, pos) { + // Heuristic: if the query string is all lowercase, do a case insensitive search. + return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase()); + } + function dialog(cm, text, shortText, f) { + if (cm.openDialog) cm.openDialog(text, f); + else f(prompt(shortText, "")); + } + function confirmDialog(cm, text, shortText, fs) { + if (cm.openConfirm) cm.openConfirm(text, fs); + else if (confirm(shortText)) fs[0](); + } + function parseQuery(query) { + var isRE = query.match(/^\/(.*)\/([a-z]*)$/); + return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query; + } + var queryDialog = + 'Search: (Use /re/ syntax for regexp search)'; + function doSearch(cm, rev) { + var state = getSearchState(cm); + if (state.query) return findNext(cm, rev); + dialog(cm, queryDialog, "Search for:", function(query) { + cm.operation(function() { + if (!query || state.query) return; + state.query = parseQuery(query); + cm.removeOverlay(state.overlay); + state.overlay = searchOverlay(query); + cm.addOverlay(state.overlay); + state.posFrom = state.posTo = cm.getCursor(); + findNext(cm, rev); + }); + }); + } + function findNext(cm, rev) {cm.operation(function() { + var state = getSearchState(cm); + var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); + if (!cursor.find(rev)) { + cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0)); + if (!cursor.find(rev)) return; + } + cm.setSelection(cursor.from(), cursor.to()); + state.posFrom = cursor.from(); state.posTo = cursor.to(); + });} + function clearSearch(cm) {cm.operation(function() { + var state = getSearchState(cm); + if (!state.query) return; + state.query = null; + cm.removeOverlay(state.overlay); + });} + + var replaceQueryDialog = + 'Replace: (Use /re/ syntax for regexp search)'; + var replacementQueryDialog = 'With: '; + var doReplaceConfirm = "Replace? "; + function replace(cm, all) { + dialog(cm, replaceQueryDialog, "Replace:", function(query) { + if (!query) return; + query = parseQuery(query); + dialog(cm, replacementQueryDialog, "Replace with:", function(text) { + if (all) { + cm.operation(function() { + for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { + if (typeof query != "string") { + var match = cm.getRange(cursor.from(), cursor.to()).match(query); + cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];})); + } else cursor.replace(text); + } + }); + } else { + clearSearch(cm); + var cursor = getSearchCursor(cm, query, cm.getCursor()); + var advance = function() { + var start = cursor.from(), match; + if (!(match = cursor.findNext())) { + cursor = getSearchCursor(cm, query); + if (!(match = cursor.findNext()) || + (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return; + } + cm.setSelection(cursor.from(), cursor.to()); + confirmDialog(cm, doReplaceConfirm, "Replace?", + [function() {doReplace(match);}, advance]); + }; + var doReplace = function(match) { + cursor.replace(typeof query == "string" ? text : + text.replace(/\$(\d)/, function(_, i) {return match[i];})); + advance(); + }; + advance(); + } + }); + }); + } + + CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; + CodeMirror.commands.findNext = doSearch; + CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; + CodeMirror.commands.clearSearch = clearSearch; + CodeMirror.commands.replace = replace; + CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; +})(); diff --git a/gulliver/js/codemirror/addon/search/searchcursor.js b/gulliver/js/codemirror/addon/search/searchcursor.js new file mode 100644 index 000000000..fd134636e --- /dev/null +++ b/gulliver/js/codemirror/addon/search/searchcursor.js @@ -0,0 +1,130 @@ +(function(){ + var Pos = CodeMirror.Pos; + + function SearchCursor(cm, query, pos, caseFold) { + this.atOccurrence = false; this.cm = cm; + if (caseFold == null && typeof query == "string") caseFold = false; + + pos = pos ? cm.clipPos(pos) : Pos(0, 0); + this.pos = {from: pos, to: pos}; + + // The matches method is filled in based on the type of query. + // It takes a position and a direction, and returns an object + // describing the next occurrence of the query, or null if no + // more matches were found. + if (typeof query != "string") { // Regexp match + if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g"); + this.matches = function(reverse, pos) { + if (reverse) { + query.lastIndex = 0; + var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; + for (;;) { + query.lastIndex = cutOff; + var newMatch = query.exec(line); + if (!newMatch) break; + match = newMatch; + start = match.index; + cutOff = match.index + 1; + } + } else { + query.lastIndex = pos.ch; + var line = cm.getLine(pos.line), match = query.exec(line), + start = match && match.index; + } + if (match && match[0]) + return {from: Pos(pos.line, start), + to: Pos(pos.line, start + match[0].length), + match: match}; + }; + } else { // String query + if (caseFold) query = query.toLowerCase(); + var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; + var target = query.split("\n"); + // Different methods for single-line and multi-line queries + if (target.length == 1) { + if (!query.length) { + // Empty string would match anything and never progress, so + // we define it to match nothing instead. + this.matches = function() {}; + } else { + this.matches = function(reverse, pos) { + var line = fold(cm.getLine(pos.line)), len = query.length, match; + if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) + : (match = line.indexOf(query, pos.ch)) != -1) + return {from: Pos(pos.line, match), + to: Pos(pos.line, match + len)}; + }; + } + } else { + this.matches = function(reverse, pos) { + var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); + var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); + if (reverse ? offsetA >= pos.ch || offsetA != match.length + : offsetA <= pos.ch || offsetA != line.length - match.length) + return; + for (;;) { + if (reverse ? !ln : ln == cm.lineCount() - 1) return; + line = fold(cm.getLine(ln += reverse ? -1 : 1)); + match = target[reverse ? --idx : ++idx]; + if (idx > 0 && idx < target.length - 1) { + if (line != match) return; + else continue; + } + var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); + if (reverse ? offsetB != line.length - match.length : offsetB != match.length) + return; + var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); + return {from: reverse ? end : start, to: reverse ? start : end}; + } + }; + } + } + } + + SearchCursor.prototype = { + findNext: function() {return this.find(false);}, + findPrevious: function() {return this.find(true);}, + + find: function(reverse) { + var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); + function savePosAndFail(line) { + var pos = Pos(line, 0); + self.pos = {from: pos, to: pos}; + self.atOccurrence = false; + return false; + } + + for (;;) { + if (this.pos = this.matches(reverse, pos)) { + if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); } + this.atOccurrence = true; + return this.pos.match || true; + } + if (reverse) { + if (!pos.line) return savePosAndFail(0); + pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length); + } + else { + var maxLine = this.cm.lineCount(); + if (pos.line == maxLine - 1) return savePosAndFail(maxLine); + pos = Pos(pos.line + 1, 0); + } + } + }, + + from: function() {if (this.atOccurrence) return this.pos.from;}, + to: function() {if (this.atOccurrence) return this.pos.to;}, + + replace: function(newText) { + if (!this.atOccurrence) return; + var lines = CodeMirror.splitLines(newText); + this.cm.replaceRange(lines, this.pos.from, this.pos.to); + this.pos.to = Pos(this.pos.from.line + lines.length - 1, + lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); + } + }; + + CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { + return new SearchCursor(this, query, pos, caseFold); + }); +})(); diff --git a/gulliver/js/codemirror/addon/selection/active-line.js b/gulliver/js/codemirror/addon/selection/active-line.js new file mode 100644 index 000000000..988a0ffbf --- /dev/null +++ b/gulliver/js/codemirror/addon/selection/active-line.js @@ -0,0 +1,39 @@ +// Because sometimes you need to style the cursor's line. +// +// Adds an option 'styleActiveLine' which, when enabled, gives the +// active line's wrapping
the CSS class "CodeMirror-activeline", +// and gives its background
the class "CodeMirror-activeline-background". + +(function() { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + updateActiveLine(cm); + cm.on("cursorActivity", updateActiveLine); + } else if (!val && prev) { + cm.off("cursorActivity", updateActiveLine); + clearActiveLine(cm); + delete cm._activeLine; + } + }); + + function clearActiveLine(cm) { + if ("_activeLine" in cm) { + cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS); + cm.removeLineClass(cm._activeLine, "background", BACK_CLASS); + } + } + + function updateActiveLine(cm) { + var line = cm.getLineHandle(cm.getCursor().line); + if (cm._activeLine == line) return; + clearActiveLine(cm); + cm.addLineClass(line, "wrap", WRAP_CLASS); + cm.addLineClass(line, "background", BACK_CLASS); + cm._activeLine = line; + } +})(); diff --git a/gulliver/js/codemirror/addon/selection/mark-selection.js b/gulliver/js/codemirror/addon/selection/mark-selection.js new file mode 100644 index 000000000..d7ff30c9a --- /dev/null +++ b/gulliver/js/codemirror/addon/selection/mark-selection.js @@ -0,0 +1,34 @@ +// Because sometimes you need to mark the selected *text*. +// +// Adds an option 'styleSelectedText' which, when enabled, gives +// selected text the CSS class "CodeMirror-selectedtext". + +(function() { + "use strict"; + + CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + updateSelectedText(cm); + cm.on("cursorActivity", updateSelectedText); + } else if (!val && prev) { + cm.off("cursorActivity", updateSelectedText); + clearSelectedText(cm); + delete cm._selectionMark; + } + }); + + function clearSelectedText(cm) { + if (cm._selectionMark) cm._selectionMark.clear(); + } + + function updateSelectedText(cm) { + clearSelectedText(cm); + + if (cm.somethingSelected()) + cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"), + {className: "CodeMirror-selectedtext"}); + else + cm._selectionMark = null; + } +})(); diff --git a/gulliver/js/codemirror/demo/completePHP.html b/gulliver/js/codemirror/demo/completePHP.html index d6eea91f7..9fcd486c0 100644 --- a/gulliver/js/codemirror/demo/completePHP.html +++ b/gulliver/js/codemirror/demo/completePHP.html @@ -22,21 +22,19 @@

CodeMirror: Autocomplete demo

- + + +

Press ctrl-space to activate autocompletion. See the code (here and here) to figure out @@ -46,14 +44,38 @@ how it works.

CodeMirror.commands.autocomplete = function(cm) { CodeMirror.showHint(cm, CodeMirror.phpHint); } + var editor = CodeMirror.fromTextArea(document.getElementById("code"), { lineNumbers: true, matchBrackets: true, mode: "application/x-httpd-php", + PMEnabled: true, indentUnit: 2, - indentWithTabs: true, + indentWithTabs: false, extraKeys: {"Ctrl-Space": "autocomplete"} }); + editor.save(); + //editor.toTextArea(); //reset to the textArea + alert(editor.getTextArea().value); + editor.focus(); + CodeMirror.commands.selectAll(editor); + +function gettext() { + var cur = editor.getCursor(); + var text1 = editor.getLine(cur.line); + alert("text: ''" + text1 + "''"); +} + +function insertText() { + var cur = editor.getCursor(); + editor.replaceRange("******", cur); +} + +function mark() { + var from = editor.getCursor(); + var to ; + editor.replaceSelection("$$$$$$$"); +} diff --git a/gulliver/js/codemirror/mode/php/php.js b/gulliver/js/codemirror/mode/php/php.js index 56497ed43..3992dc0ee 100644 --- a/gulliver/js/codemirror/mode/php/php.js +++ b/gulliver/js/codemirror/mode/php/php.js @@ -63,6 +63,11 @@ state.curState = state.php; return "meta"; } + if( config.PMEnabled == true) { + state.curMode = phpMode; + state.curState = state.php; + return "meta"; + } if (state.pending == '"') { while (!stream.eol() && stream.next() != '"') {} var style = "string";