From 3a754133dbc138390503341fd2e9beba3e43aa4b Mon Sep 17 00:00:00 2001
From: Jose Antonio Marquez
Date: Fri, 27 Jan 2012 12:05:17 -0800
Subject: Merged old FileIO
---
imports/codemirror/lib/util/dialog.css | 23 ++
imports/codemirror/lib/util/dialog.js | 63 ++++++
imports/codemirror/lib/util/foldcode.js | 66 ++++++
imports/codemirror/lib/util/formatting.js | 291 +++++++++++++++++++++++++
imports/codemirror/lib/util/javascript-hint.js | 83 +++++++
imports/codemirror/lib/util/overlay.js | 51 +++++
imports/codemirror/lib/util/runmode.js | 27 +++
imports/codemirror/lib/util/search.js | 114 ++++++++++
imports/codemirror/lib/util/searchcursor.js | 117 ++++++++++
imports/codemirror/lib/util/simple-hint.css | 16 ++
imports/codemirror/lib/util/simple-hint.js | 66 ++++++
11 files changed, 917 insertions(+)
create mode 100755 imports/codemirror/lib/util/dialog.css
create mode 100755 imports/codemirror/lib/util/dialog.js
create mode 100755 imports/codemirror/lib/util/foldcode.js
create mode 100755 imports/codemirror/lib/util/formatting.js
create mode 100755 imports/codemirror/lib/util/javascript-hint.js
create mode 100755 imports/codemirror/lib/util/overlay.js
create mode 100755 imports/codemirror/lib/util/runmode.js
create mode 100755 imports/codemirror/lib/util/search.js
create mode 100755 imports/codemirror/lib/util/searchcursor.js
create mode 100755 imports/codemirror/lib/util/simple-hint.css
create mode 100755 imports/codemirror/lib/util/simple-hint.js
(limited to 'imports/codemirror/lib/util')
diff --git a/imports/codemirror/lib/util/dialog.css b/imports/codemirror/lib/util/dialog.css
new file mode 100755
index 00000000..4cb467ef
--- /dev/null
+++ b/imports/codemirror/lib/util/dialog.css
@@ -0,0 +1,23 @@
+.CodeMirror-dialog {
+ position: relative;
+}
+
+.CodeMirror-dialog > div {
+ position: absolute;
+ top: 0; left: 0; right: 0;
+ background: white;
+ border-bottom: 1px solid #eee;
+ z-index: 15;
+ padding: .1em .8em;
+ overflow: hidden;
+ color: #333;
+}
+
+.CodeMirror-dialog input {
+ border: none;
+ outline: none;
+ background: transparent;
+ width: 20em;
+ color: inherit;
+ font-family: monospace;
+}
diff --git a/imports/codemirror/lib/util/dialog.js b/imports/codemirror/lib/util/dialog.js
new file mode 100755
index 00000000..8950bf0c
--- /dev/null
+++ b/imports/codemirror/lib/util/dialog.js
@@ -0,0 +1,63 @@
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+(function() {
+ function dialogDiv(cm, template) {
+ var wrap = cm.getWrapperElement();
+ var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild);
+ dialog.className = "CodeMirror-dialog";
+ dialog.innerHTML = '
' + template + '
';
+ return dialog;
+ }
+
+ CodeMirror.defineExtension("openDialog", function(template, callback) {
+ var dialog = dialogDiv(this, template);
+ var closed = false, me = this;
+ function close() {
+ if (closed) return;
+ closed = true;
+ dialog.parentNode.removeChild(dialog);
+ }
+ var inp = dialog.getElementsByTagName("input")[0];
+ if (inp) {
+ CodeMirror.connect(inp, "keydown", function(e) {
+ if (e.keyCode == 13 || e.keyCode == 27) {
+ CodeMirror.e_stop(e);
+ close();
+ me.focus();
+ if (e.keyCode == 13) callback(inp.value);
+ }
+ });
+ inp.focus();
+ CodeMirror.connect(inp, "blur", close);
+ }
+ return close;
+ });
+
+ CodeMirror.defineExtension("openConfirm", function(template, callbacks) {
+ var dialog = dialogDiv(this, template);
+ 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.connect(b, "click", function(e) {
+ CodeMirror.e_preventDefault(e);
+ close();
+ if (callback) callback(me);
+ });
+ })(callbacks[i]);
+ CodeMirror.connect(b, "blur", function() {
+ --blurring;
+ setTimeout(function() { if (blurring <= 0) close(); }, 200);
+ });
+ CodeMirror.connect(b, "focus", function() { ++blurring; });
+ }
+ });
+})();
\ No newline at end of file
diff --git a/imports/codemirror/lib/util/foldcode.js b/imports/codemirror/lib/util/foldcode.js
new file mode 100755
index 00000000..18957792
--- /dev/null
+++ b/imports/codemirror/lib/util/foldcode.js
@@ -0,0 +1,66 @@
+CodeMirror.braceRangeFinder = function(cm, line) {
+ var lineText = cm.getLine(line);
+ var startChar = lineText.lastIndexOf("{");
+ if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return;
+ var tokenType = cm.getTokenAt({line: line, ch: startChar}).className;
+ var count = 1, lastLine = cm.lineCount(), end;
+ 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({line: i, ch: pos + 1}).className == tokenType) {
+ if (pos == nextOpen) ++count;
+ else if (!--count) { end = i; break outer; }
+ }
+ ++pos;
+ }
+ }
+ if (end == null || end == line + 1) return;
+ return end;
+};
+
+
+CodeMirror.newFoldFunction = function(rangeFinder, markText) {
+ var folded = [];
+ if (markText == null) markText = '▼
%N%';
+
+ function isFolded(cm, n) {
+ for (var i = 0; i < folded.length; ++i) {
+ var start = cm.lineInfo(folded[i].start);
+ if (!start) folded.splice(i--, 1);
+ else if (start.line == n) return {pos: i, region: folded[i]};
+ }
+ }
+
+ function expand(cm, region) {
+ cm.clearMarker(region.start);
+ for (var i = 0; i < region.hidden.length; ++i)
+ cm.showLine(region.hidden[i]);
+ }
+
+ return function(cm, line) {
+ cm.operation(function() {
+ var known = isFolded(cm, line);
+ if (known) {
+ folded.splice(known.pos, 1);
+ expand(cm, known.region);
+ } else {
+ var end = rangeFinder(cm, line);
+ if (end == null) return;
+ var hidden = [];
+ for (var i = line + 1; i < end; ++i) {
+ var handle = cm.hideLine(i);
+ if (handle) hidden.push(handle);
+ }
+ var first = cm.setMarker(line, markText);
+ var region = {start: first, hidden: hidden};
+ cm.onDeleteLine(first, function() { expand(cm, region); });
+ folded.push(region);
+ }
+ });
+ };
+};
diff --git a/imports/codemirror/lib/util/formatting.js b/imports/codemirror/lib/util/formatting.js
new file mode 100755
index 00000000..986bcb8f
--- /dev/null
+++ b/imports/codemirror/lib/util/formatting.js
@@ -0,0 +1,291 @@
+// ============== Formatting extensions ============================
+// A common storage for all mode-specific formatting features
+if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
+
+// Returns the extension of the editor's current mode
+CodeMirror.defineExtension("getModeExt", function () {
+ return CodeMirror.modeExtensions[this.getOption("mode")];
+});
+
+// If the current mode is 'htmlmixed', returns the extension of a mode located at
+// the specified position (can be htmlmixed, css or javascript). Otherwise, simply
+// returns the extension of the editor's current mode.
+CodeMirror.defineExtension("getModeExtAtPos", function (pos) {
+ var token = this.getTokenAt(pos);
+ if (token && token.state && token.state.mode)
+ return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode];
+ else
+ return this.getModeExt();
+});
+
+// Comment/uncomment the specified range
+CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
+ var curMode = this.getModeExtAtPos(this.getCursor());
+ if (isComment) { // Comment range
+ var commentedText = this.getRange(from, to);
+ this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd
+ , from, to);
+ if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside
+ this.setCursor(from.line, from.ch + curMode.commentStart.length);
+ }
+ }
+ else { // Uncomment range
+ var selText = this.getRange(from, to);
+ var startIndex = selText.indexOf(curMode.commentStart);
+ var endIndex = selText.lastIndexOf(curMode.commentEnd);
+ if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
+ // Take string till comment start
+ selText = selText.substr(0, startIndex)
+ // From comment start till comment end
+ + selText.substring(startIndex + curMode.commentStart.length, endIndex)
+ // From comment end till string end
+ + selText.substr(endIndex + curMode.commentEnd.length);
+ }
+ this.replaceRange(selText, from, to);
+ }
+});
+
+// Applies automatic mode-aware indentation to the specified range
+CodeMirror.defineExtension("autoIndentRange", function (from, to) {
+ var cmInstance = this;
+ this.operation(function () {
+ for (var i = from.line; i <= to.line; i++) {
+ cmInstance.indentLine(i);
+ }
+ });
+});
+
+// Applies automatic formatting to the specified range
+CodeMirror.defineExtension("autoFormatRange", function (from, to) {
+ var absStart = this.indexFromPos(from);
+ var absEnd = this.indexFromPos(to);
+ // Insert additional line breaks where necessary according to the
+ // mode's syntax
+ var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd);
+ var cmInstance = this;
+
+ // Replace and auto-indent the range
+ this.operation(function () {
+ cmInstance.replaceRange(res, from, to);
+ var startLine = cmInstance.posFromIndex(absStart).line;
+ var endLine = cmInstance.posFromIndex(absStart + res.length).line;
+ for (var i = startLine; i <= endLine; i++) {
+ cmInstance.indentLine(i);
+ }
+ });
+});
+
+// Define extensions for a few modes
+
+CodeMirror.modeExtensions["css"] = {
+ commentStart: "/*",
+ commentEnd: "*/",
+ wordWrapChars: [";", "\\{", "\\}"],
+ autoFormatLineBreaks: function (text) {
+ return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
+ }
+};
+
+CodeMirror.modeExtensions["javascript"] = {
+ commentStart: "/*",
+ commentEnd: "*/",
+ wordWrapChars: [";", "\\{", "\\}"],
+
+ getNonBreakableBlocks: function (text) {
+ var nonBreakableRegexes = [
+ new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"),
+ new RegExp("'([\\s\\S]*?)('|$)"),
+ new RegExp("\"([\\s\\S]*?)(\"|$)"),
+ new RegExp("//.*([\r\n]|$)")
+ ];
+ var nonBreakableBlocks = new Array();
+ for (var i = 0; i < nonBreakableRegexes.length; i++) {
+ var curPos = 0;
+ while (curPos < text.length) {
+ var m = text.substr(curPos).match(nonBreakableRegexes[i]);
+ if (m != null) {
+ nonBreakableBlocks.push({
+ start: curPos + m.index,
+ end: curPos + m.index + m[0].length
+ });
+ curPos += m.index + Math.max(1, m[0].length);
+ }
+ else { // No more matches
+ break;
+ }
+ }
+ }
+ nonBreakableBlocks.sort(function (a, b) {
+ return a.start - b.start;
+ });
+
+ return nonBreakableBlocks;
+ },
+
+ autoFormatLineBreaks: function (text) {
+ var curPos = 0;
+ var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g");
+ var nonBreakableBlocks = this.getNonBreakableBlocks(text);
+ if (nonBreakableBlocks != null) {
+ var res = "";
+ for (var i = 0; i < nonBreakableBlocks.length; i++) {
+ if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
+ res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
+ curPos = nonBreakableBlocks[i].start;
+ }
+ if (nonBreakableBlocks[i].start <= curPos
+ && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
+ res += text.substring(curPos, nonBreakableBlocks[i].end);
+ curPos = nonBreakableBlocks[i].end;
+ }
+ }
+ if (curPos < text.length - 1) {
+ res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
+ }
+ return res;
+ }
+ else {
+ return text.replace(reLinesSplitter, "$1\n$2");
+ }
+ }
+};
+
+CodeMirror.modeExtensions["xml"] = {
+ commentStart: "",
+ wordWrapChars: [">"],
+
+ autoFormatLineBreaks: function (text) {
+ var lines = text.split("\n");
+ var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
+ var reOpenBrackets = new RegExp("<", "g");
+ var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
+ for (var i = 0; i < lines.length; i++) {
+ var mToProcess = lines[i].match(reProcessedPortion);
+ if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
+ lines[i] = mToProcess[1]
+ + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2")
+ + mToProcess[3];
+ continue;
+ }
+ }
+
+ return lines.join("\n");
+ }
+};
+
+CodeMirror.modeExtensions["htmlmixed"] = {
+ commentStart: "",
+ wordWrapChars: [">", ";", "\\{", "\\}"],
+
+ getModeInfos: function (text, absPos) {
+ var modeInfos = new Array();
+ modeInfos[0] =
+ {
+ pos: 0,
+ modeExt: CodeMirror.modeExtensions["xml"],
+ modeName: "xml"
+ };
+
+ var modeMatchers = new Array();
+ modeMatchers[0] =
+ {
+ regex: new RegExp("]*>|$)", "i"),
+ modeExt: CodeMirror.modeExtensions["css"],
+ modeName: "css"
+ };
+ modeMatchers[1] =
+ {
+ regex: new RegExp("]*>|$)", "i"),
+ modeExt: CodeMirror.modeExtensions["javascript"],
+ modeName: "javascript"
+ };
+
+ var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1);
+ // Detect modes for the entire text
+ for (var i = 0; i < modeMatchers.length; i++) {
+ var curPos = 0;
+ while (curPos <= lastCharPos) {
+ var m = text.substr(curPos).match(modeMatchers[i].regex);
+ if (m != null) {
+ if (m.length > 1 && m[1].length > 0) {
+ // Push block begin pos
+ var blockBegin = curPos + m.index + m[0].indexOf(m[1]);
+ modeInfos.push(
+ {
+ pos: blockBegin,
+ modeExt: modeMatchers[i].modeExt,
+ modeName: modeMatchers[i].modeName
+ });
+ // Push block end pos
+ modeInfos.push(
+ {
+ pos: blockBegin + m[1].length,
+ modeExt: modeInfos[0].modeExt,
+ modeName: modeInfos[0].modeName
+ });
+ curPos += m.index + m[0].length;
+ continue;
+ }
+ else {
+ curPos += m.index + Math.max(m[0].length, 1);
+ }
+ }
+ else { // No more matches
+ break;
+ }
+ }
+ }
+ // Sort mode infos
+ modeInfos.sort(function sortModeInfo(a, b) {
+ return a.pos - b.pos;
+ });
+
+ return modeInfos;
+ },
+
+ autoFormatLineBreaks: function (text, startPos, endPos) {
+ var modeInfos = this.getModeInfos(text);
+ var reBlockStartsWithNewline = new RegExp("^\\s*?\n");
+ var reBlockEndsWithNewline = new RegExp("\n\\s*?$");
+ var res = "";
+ // Use modes info to break lines correspondingly
+ if (modeInfos.length > 1) { // Deal with multi-mode text
+ for (var i = 1; i <= modeInfos.length; i++) {
+ var selStart = modeInfos[i - 1].pos;
+ var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos);
+
+ if (selStart >= endPos) { // The block starts later than the needed fragment
+ break;
+ }
+ if (selStart < startPos) {
+ if (selEnd <= startPos) { // The block starts earlier than the needed fragment
+ continue;
+ }
+ selStart = startPos;
+ }
+ if (selEnd > endPos) {
+ selEnd = endPos;
+ }
+ var textPortion = text.substring(selStart, selEnd);
+ if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block
+ if (!reBlockStartsWithNewline.test(textPortion)
+ && selStart > 0) { // The block does not start with a line break
+ textPortion = "\n" + textPortion;
+ }
+ if (!reBlockEndsWithNewline.test(textPortion)
+ && selEnd < text.length - 1) { // The block does not end with a line break
+ textPortion += "\n";
+ }
+ }
+ res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion);
+ }
+ }
+ else { // Single-mode text
+ res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos));
+ }
+
+ return res;
+ }
+};
diff --git a/imports/codemirror/lib/util/javascript-hint.js b/imports/codemirror/lib/util/javascript-hint.js
new file mode 100755
index 00000000..4e88a7e4
--- /dev/null
+++ b/imports/codemirror/lib/util/javascript-hint.js
@@ -0,0 +1,83 @@
+(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;
+ }
+
+ CodeMirror.javascriptHint = function(editor) {
+ // Find the token at the cursor
+ var cur = editor.getCursor(), token = editor.getTokenAt(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 == "." ? "property" : null};
+ }
+ // If it is a property, find out what it is a property of.
+ while (tprop.className == "property") {
+ tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
+ if (tprop.string != ".") return;
+ tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
+ if (!context) var context = [];
+ context.push(tprop);
+ }
+ return {list: getCompletions(token, context),
+ from: {line: cur.line, ch: token.start},
+ to: {line: cur.line, ch: token.end}};
+ }
+
+ 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 keywords = ("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(" ");
+
+ 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 (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.className == "variable")
+ base = window[obj.string];
+ else if (obj.className == "string")
+ base = "";
+ else if (obj.className == "atom")
+ base = 1;
+ 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 variables)
+ for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
+ gatherCompletions(window);
+ forEach(keywords, maybeAdd);
+ }
+ return found;
+ }
+})();
diff --git a/imports/codemirror/lib/util/overlay.js b/imports/codemirror/lib/util/overlay.js
new file mode 100755
index 00000000..c4cdf9fc
--- /dev/null
+++ b/imports/codemirror/lib/util/overlay.js
@@ -0,0 +1,51 @@
+// 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.
+
+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: function(state, textAfter) {
+ return base.indent(state.base, textAfter);
+ },
+ electricChars: base.electricChars
+ };
+};
diff --git a/imports/codemirror/lib/util/runmode.js b/imports/codemirror/lib/util/runmode.js
new file mode 100755
index 00000000..de4a7602
--- /dev/null
+++ b/imports/codemirror/lib/util/runmode.js
@@ -0,0 +1,27 @@
+CodeMirror.runMode = function(string, modespec, callback) {
+ var mode = CodeMirror.getMode({indentUnit: 2}, modespec);
+ var isNode = callback.nodeType == 1;
+ if (isNode) {
+ var node = callback, accum = [];
+ callback = function(string, style) {
+ if (string == "\n")
+ accum.push("
");
+ else if (style)
+ accum.push("" + CodeMirror.htmlEscape(string) + "");
+ else
+ accum.push(CodeMirror.htmlEscape(string));
+ }
+ }
+ 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;
+ }
+ }
+ if (isNode)
+ node.innerHTML = accum.join("");
+};
diff --git a/imports/codemirror/lib/util/search.js b/imports/codemirror/lib/util/search.js
new file mode 100755
index 00000000..63ebca9b
--- /dev/null
+++ b/imports/codemirror/lib/util/search.js
@@ -0,0 +1,114 @@
+// 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 SearchState() {
+ this.posFrom = this.posTo = this.query = null;
+ this.marked = [];
+ }
+ function getSearchState(cm) {
+ return cm._searchState || (cm._searchState = new SearchState());
+ }
+ 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(/^\/(.*)\/$/);
+ return isRE ? new RegExp(isRE[1]) : 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);
+ if (cm.lineCount() < 2000) { // This is too expensive on big documents.
+ for (var cursor = cm.getSearchCursor(query); cursor.findNext();)
+ state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
+ }
+ state.posFrom = state.posTo = cm.getCursor();
+ findNext(cm, rev);
+ });
+ });
+ }
+ function findNext(cm, rev) {cm.operation(function() {
+ var state = getSearchState(cm);
+ var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo);
+ if (!cursor.find(rev)) {
+ cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 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;
+ for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
+ state.marked.length = 0;
+ })}
+
+ 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 = cm.getSearchCursor(query); cursor.findNext();) {
+ if (typeof query != "string") {
+ var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+ cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
+ } else cursor.replace(text);
+ }
+ });
+ } else {
+ clearSearch(cm);
+ var cursor = cm.getSearchCursor(query, cm.getCursor());
+ function advance() {
+ var start = cursor.from(), match;
+ if (!(match = cursor.findNext())) {
+ cursor = cm.getSearchCursor(query);
+ if (!(match = cursor.findNext()) ||
+ (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]);
+ }
+ function doReplace(match) {
+ cursor.replace(typeof query == "string" ? text :
+ text.replace(/\$(\d)/, function(w, 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/imports/codemirror/lib/util/searchcursor.js b/imports/codemirror/lib/util/searchcursor.js
new file mode 100755
index 00000000..3b77829f
--- /dev/null
+++ b/imports/codemirror/lib/util/searchcursor.js
@@ -0,0 +1,117 @@
+(function(){
+ function SearchCursor(cm, query, pos, caseFold) {
+ this.atOccurrence = false; this.cm = cm;
+ if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
+
+ pos = pos ? cm.clipPos(pos) : {line: 0, ch: 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
+ this.matches = function(reverse, pos) {
+ if (reverse) {
+ var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0;
+ while (match) {
+ var ind = line.indexOf(match[0]);
+ start += ind;
+ line = line.slice(ind + 1);
+ var newmatch = line.match(query);
+ if (newmatch) match = newmatch;
+ else break;
+ start++;
+ }
+ }
+ else {
+ var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query),
+ start = match && pos.ch + line.indexOf(match[0]);
+ }
+ if (match)
+ return {from: {line: pos.line, ch: start},
+ to: {line: pos.line, ch: 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)
+ 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: {line: pos.line, ch: match},
+ to: {line: pos.line, ch: 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 = {line: pos.line, ch: offsetA}, end = {line: ln, ch: 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 = {line: line, ch: 0};
+ self.pos = {from: pos, to: pos};
+ self.atOccurrence = false;
+ return false;
+ }
+
+ for (;;) {
+ if (this.pos = this.matches(reverse, pos)) {
+ this.atOccurrence = true;
+ return this.pos.match || true;
+ }
+ if (reverse) {
+ if (!pos.line) return savePosAndFail(0);
+ pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
+ }
+ else {
+ var maxLine = this.cm.lineCount();
+ if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
+ pos = {line: pos.line+1, ch: 0};
+ }
+ }
+ },
+
+ from: function() {if (this.atOccurrence) return this.pos.from;},
+ to: function() {if (this.atOccurrence) return this.pos.to;},
+
+ replace: function(newText) {
+ var self = this;
+ if (this.atOccurrence)
+ self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
+ }
+ };
+
+ CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
+ return new SearchCursor(this, query, pos, caseFold);
+ });
+})();
diff --git a/imports/codemirror/lib/util/simple-hint.css b/imports/codemirror/lib/util/simple-hint.css
new file mode 100755
index 00000000..4387cb94
--- /dev/null
+++ b/imports/codemirror/lib/util/simple-hint.css
@@ -0,0 +1,16 @@
+.CodeMirror-completions {
+ position: absolute;
+ z-index: 10;
+ overflow: hidden;
+ -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+}
+.CodeMirror-completions select {
+ background: #fafafa;
+ outline: none;
+ border: none;
+ padding: 0;
+ margin: 0;
+ font-family: monospace;
+}
diff --git a/imports/codemirror/lib/util/simple-hint.js b/imports/codemirror/lib/util/simple-hint.js
new file mode 100755
index 00000000..b38f3892
--- /dev/null
+++ b/imports/codemirror/lib/util/simple-hint.js
@@ -0,0 +1,66 @@
+(function() {
+ CodeMirror.simpleHint = function(editor, getHints) {
+ // We want a single cursor position.
+ if (editor.somethingSelected()) return;
+ var result = getHints(editor);
+ if (!result || !result.list.length) return;
+ var completions = result.list;
+ function insert(str) {
+ editor.replaceRange(str, result.from, result.to);
+ }
+ // When there is only one completion, use it directly.
+ if (completions.length == 1) {insert(completions[0]); return true;}
+
+ // Build the select widget
+ var complete = document.createElement("div");
+ complete.className = "CodeMirror-completions";
+ var sel = complete.appendChild(document.createElement("select"));
+ // Opera doesn't move the selection when pressing up/down in a
+ // multi-select, but it does properly support the size property on
+ // single-selects, so no multi-select is necessary.
+ if (!window.opera) sel.multiple = true;
+ for (var i = 0; i < completions.length; ++i) {
+ var opt = sel.appendChild(document.createElement("option"));
+ opt.appendChild(document.createTextNode(completions[i]));
+ }
+ sel.firstChild.selected = true;
+ sel.size = Math.min(10, completions.length);
+ var pos = editor.cursorCoords();
+ complete.style.left = pos.x + "px";
+ complete.style.top = pos.yBot + "px";
+ document.body.appendChild(complete);
+ // Hack to hide the scrollbar.
+ if (completions.length <= 10)
+ complete.style.width = (sel.clientWidth - 1) + "px";
+
+ var done = false;
+ function close() {
+ if (done) return;
+ done = true;
+ complete.parentNode.removeChild(complete);
+ }
+ function pick() {
+ insert(completions[sel.selectedIndex]);
+ close();
+ setTimeout(function(){editor.focus();}, 50);
+ }
+ CodeMirror.connect(sel, "blur", close);
+ CodeMirror.connect(sel, "keydown", function(event) {
+ var code = event.keyCode;
+ // Enter
+ if (code == 13) {CodeMirror.e_stop(event); pick();}
+ // Escape
+ else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
+ else if (code != 38 && code != 40) {
+ close(); editor.focus();
+ setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50);
+ }
+ });
+ CodeMirror.connect(sel, "dblclick", pick);
+
+ sel.focus();
+ // Opera sometimes ignores focusing a freshly created node
+ if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
+ return true;
+ };
+})();
--
cgit v1.2.3