kit_cms 2.3.8 → 2.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/assets/javascripts/codemirror/codemirror.js +5104 -0
- data/app/assets/javascripts/codemirror/collapserange.js +68 -0
- data/app/assets/javascripts/codemirror/css.js +465 -0
- data/app/assets/javascripts/codemirror/dialog.js +76 -0
- data/app/assets/javascripts/codemirror/editor.js +19 -0
- data/app/assets/javascripts/codemirror/foldcode.js +183 -0
- data/app/assets/javascripts/codemirror/formatting.js +108 -0
- data/app/assets/javascripts/codemirror/htmlmixed.js +84 -0
- data/app/assets/javascripts/codemirror/javascript.js +422 -0
- data/app/assets/javascripts/codemirror/jquery.js +4 -0
- data/app/assets/javascripts/codemirror/loadmode.js +51 -0
- data/app/assets/javascripts/codemirror/match-highlighter.js +46 -0
- data/app/assets/javascripts/codemirror/matchbrackets.js +63 -0
- data/app/assets/javascripts/codemirror/multiplex.js +95 -0
- data/app/assets/javascripts/codemirror/overlay.js +59 -0
- data/app/assets/javascripts/codemirror/search.js +131 -0
- data/app/assets/javascripts/codemirror/searchcursor.js +131 -0
- data/app/assets/javascripts/codemirror/xml.js +324 -0
- data/app/assets/stylesheets/codemirror/dialog.css +32 -0
- data/app/assets/stylesheets/codemirror/style.css +242 -0
- data/app/assets/stylesheets/codemirror/theme.css +25 -0
- data/app/views/layouts/_html-editor.html.haml +1 -0
- data/app/views/layouts/mercury-editor.html.erb +5 -5
- data/app/views/utility/mercury_html.html.haml +28 -3
- metadata +41 -39
- data/app/assets/javascripts/kit/markitup/jquery.markitup.js +0 -634
- data/app/assets/javascripts/kit/markitup/settings.js +0 -31
- data/app/assets/stylesheets/kit/markitup/html.css +0 -59
- data/app/assets/stylesheets/kit/markitup/images/bg-container.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-bbcode.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-dotclear.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-html.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-json.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-markdown.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-textile.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-wiki.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor-xml.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bg-editor.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/bold.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/clean.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/h1.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/h2.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/h3.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/h4.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/h5.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/h6.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/handle.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/image.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/italic.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/link.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/list-bullet.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/list-item.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/list-numeric.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/menu.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/paragraph.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/picture.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/preview.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/stroke.png +0 -0
- data/app/assets/stylesheets/kit/markitup/images/submenu.png +0 -0
- data/app/assets/stylesheets/kit/markitup/style.css +0 -145
- data/lib/kit_cms/version.rb +0 -3
@@ -0,0 +1,46 @@
|
|
1
|
+
// Define match-highlighter commands. Depends on searchcursor.js
|
2
|
+
// Use by attaching the following function call to the cursorActivity event:
|
3
|
+
//myCodeMirror.matchHighlight(minChars);
|
4
|
+
// And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html)
|
5
|
+
|
6
|
+
(function() {
|
7
|
+
var DEFAULT_MIN_CHARS = 2;
|
8
|
+
|
9
|
+
function MatchHighlightState() {
|
10
|
+
this.marked = [];
|
11
|
+
}
|
12
|
+
function getMatchHighlightState(cm) {
|
13
|
+
return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState());
|
14
|
+
}
|
15
|
+
|
16
|
+
function clearMarks(cm) {
|
17
|
+
var state = getMatchHighlightState(cm);
|
18
|
+
for (var i = 0; i < state.marked.length; ++i)
|
19
|
+
state.marked[i].clear();
|
20
|
+
state.marked = [];
|
21
|
+
}
|
22
|
+
|
23
|
+
function markDocument(cm, className, minChars) {
|
24
|
+
clearMarks(cm);
|
25
|
+
minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS);
|
26
|
+
if (cm.somethingSelected() && cm.getSelection().replace(/^\s+|\s+$/g, "").length >= minChars) {
|
27
|
+
var state = getMatchHighlightState(cm);
|
28
|
+
var query = cm.getSelection();
|
29
|
+
cm.operation(function() {
|
30
|
+
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
|
31
|
+
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
|
32
|
+
//Only apply matchhighlight to the matches other than the one actually selected
|
33
|
+
if (cursor.from().line !== cm.getCursor(true).line ||
|
34
|
+
cursor.from().ch !== cm.getCursor(true).ch)
|
35
|
+
state.marked.push(cm.markText(cursor.from(), cursor.to(),
|
36
|
+
{className: className}));
|
37
|
+
}
|
38
|
+
}
|
39
|
+
});
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
CodeMirror.defineExtension("matchHighlight", function(className, minChars) {
|
44
|
+
markDocument(this, className, minChars);
|
45
|
+
});
|
46
|
+
})();
|
@@ -0,0 +1,63 @@
|
|
1
|
+
(function() {
|
2
|
+
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
3
|
+
function findMatchingBracket(cm) {
|
4
|
+
var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
|
5
|
+
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
6
|
+
if (!match) return null;
|
7
|
+
var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
|
8
|
+
var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type;
|
9
|
+
|
10
|
+
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
|
11
|
+
function scan(line, lineNo, start) {
|
12
|
+
if (!line.text) return;
|
13
|
+
var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
|
14
|
+
if (start != null) pos = start + d;
|
15
|
+
for (; pos != end; pos += d) {
|
16
|
+
var ch = line.text.charAt(pos);
|
17
|
+
if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) {
|
18
|
+
var match = matching[ch];
|
19
|
+
if (match.charAt(1) == ">" == forward) stack.push(ch);
|
20
|
+
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
|
21
|
+
else if (!stack.length) return {pos: pos, match: true};
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
|
26
|
+
if (i == cur.line) found = scan(line, i, pos);
|
27
|
+
else found = scan(cm.getLineHandle(i), i);
|
28
|
+
if (found) break;
|
29
|
+
}
|
30
|
+
return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match};
|
31
|
+
}
|
32
|
+
|
33
|
+
function matchBrackets(cm, autoclear) {
|
34
|
+
var found = findMatchingBracket(cm);
|
35
|
+
if (!found) return;
|
36
|
+
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
37
|
+
var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1},
|
38
|
+
{className: style});
|
39
|
+
var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1},
|
40
|
+
{className: style});
|
41
|
+
var clear = function() {
|
42
|
+
cm.operation(function() { one.clear(); two && two.clear(); });
|
43
|
+
};
|
44
|
+
if (autoclear) setTimeout(clear, 800);
|
45
|
+
else return clear;
|
46
|
+
}
|
47
|
+
|
48
|
+
var currentlyHighlighted = null;
|
49
|
+
function doMatchBrackets(cm) {
|
50
|
+
cm.operation(function() {
|
51
|
+
if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
|
52
|
+
if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
|
53
|
+
});
|
54
|
+
}
|
55
|
+
|
56
|
+
CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
|
57
|
+
if (val) cm.on("cursorActivity", doMatchBrackets);
|
58
|
+
else cm.off("cursorActivity", doMatchBrackets);
|
59
|
+
});
|
60
|
+
|
61
|
+
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
62
|
+
CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
|
63
|
+
})();
|
@@ -0,0 +1,95 @@
|
|
1
|
+
CodeMirror.multiplexingMode = function(outer /*, others */) {
|
2
|
+
// Others should be {open, close, mode [, delimStyle]} objects
|
3
|
+
var others = Array.prototype.slice.call(arguments, 1);
|
4
|
+
var n_others = others.length;
|
5
|
+
|
6
|
+
function indexOf(string, pattern, from) {
|
7
|
+
if (typeof pattern == "string") return string.indexOf(pattern, from);
|
8
|
+
var m = pattern.exec(from ? string.slice(from) : string);
|
9
|
+
return m ? m.index + from : -1;
|
10
|
+
}
|
11
|
+
|
12
|
+
return {
|
13
|
+
startState: function() {
|
14
|
+
return {
|
15
|
+
outer: CodeMirror.startState(outer),
|
16
|
+
innerActive: null,
|
17
|
+
inner: null
|
18
|
+
};
|
19
|
+
},
|
20
|
+
|
21
|
+
copyState: function(state) {
|
22
|
+
return {
|
23
|
+
outer: CodeMirror.copyState(outer, state.outer),
|
24
|
+
innerActive: state.innerActive,
|
25
|
+
inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner)
|
26
|
+
};
|
27
|
+
},
|
28
|
+
|
29
|
+
token: function(stream, state) {
|
30
|
+
if (!state.innerActive) {
|
31
|
+
var cutOff = Infinity, oldContent = stream.string;
|
32
|
+
for (var i = 0; i < n_others; ++i) {
|
33
|
+
var other = others[i];
|
34
|
+
var found = indexOf(oldContent, other.open, stream.pos);
|
35
|
+
if (found == stream.pos) {
|
36
|
+
stream.match(other.open);
|
37
|
+
state.innerActive = other;
|
38
|
+
state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
|
39
|
+
return other.delimStyle;
|
40
|
+
} else if (found != -1 && found < cutOff) {
|
41
|
+
cutOff = found;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);
|
45
|
+
var outerToken = outer.token(stream, state.outer);
|
46
|
+
if (cutOff != Infinity) stream.string = oldContent;
|
47
|
+
return outerToken;
|
48
|
+
} else {
|
49
|
+
var curInner = state.innerActive, oldContent = stream.string;
|
50
|
+
var found = indexOf(oldContent, curInner.close, stream.pos);
|
51
|
+
if (found == stream.pos) {
|
52
|
+
stream.match(curInner.close);
|
53
|
+
state.innerActive = state.inner = null;
|
54
|
+
return curInner.delimStyle;
|
55
|
+
}
|
56
|
+
if (found > -1) stream.string = oldContent.slice(0, found);
|
57
|
+
var innerToken = curInner.mode.token(stream, state.inner);
|
58
|
+
if (found > -1) stream.string = oldContent;
|
59
|
+
var cur = stream.current(), found = cur.indexOf(curInner.close);
|
60
|
+
if (found > -1) stream.backUp(cur.length - found);
|
61
|
+
return innerToken;
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
indent: function(state, textAfter) {
|
66
|
+
var mode = state.innerActive ? state.innerActive.mode : outer;
|
67
|
+
if (!mode.indent) return CodeMirror.Pass;
|
68
|
+
return mode.indent(state.innerActive ? state.inner : state.outer, textAfter);
|
69
|
+
},
|
70
|
+
|
71
|
+
blankLine: function(state) {
|
72
|
+
var mode = state.innerActive ? state.innerActive.mode : outer;
|
73
|
+
if (mode.blankLine) {
|
74
|
+
mode.blankLine(state.innerActive ? state.inner : state.outer);
|
75
|
+
}
|
76
|
+
if (!state.innerActive) {
|
77
|
+
for (var i = 0; i < n_others; ++i) {
|
78
|
+
var other = others[i];
|
79
|
+
if (other.open === "\n") {
|
80
|
+
state.innerActive = other;
|
81
|
+
state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
} else if (mode.close === "\n") {
|
85
|
+
state.innerActive = state.inner = null;
|
86
|
+
}
|
87
|
+
},
|
88
|
+
|
89
|
+
electricChars: outer.electricChars,
|
90
|
+
|
91
|
+
innerMode: function(state) {
|
92
|
+
return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};
|
93
|
+
}
|
94
|
+
};
|
95
|
+
};
|
@@ -0,0 +1,59 @@
|
|
1
|
+
// Utility function that allows modes to be combined. The mode given
|
2
|
+
// as the base argument takes care of most of the normal mode
|
3
|
+
// functionality, but a second (typically simple) mode is used, which
|
4
|
+
// can override the style of text. Both modes get to parse all of the
|
5
|
+
// text, but when both assign a non-null style to a piece of code, the
|
6
|
+
// overlay wins, unless the combine argument was true, in which case
|
7
|
+
// the styles are combined.
|
8
|
+
|
9
|
+
// overlayParser is the old, deprecated name
|
10
|
+
CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) {
|
11
|
+
return {
|
12
|
+
startState: function() {
|
13
|
+
return {
|
14
|
+
base: CodeMirror.startState(base),
|
15
|
+
overlay: CodeMirror.startState(overlay),
|
16
|
+
basePos: 0, baseCur: null,
|
17
|
+
overlayPos: 0, overlayCur: null
|
18
|
+
};
|
19
|
+
},
|
20
|
+
copyState: function(state) {
|
21
|
+
return {
|
22
|
+
base: CodeMirror.copyState(base, state.base),
|
23
|
+
overlay: CodeMirror.copyState(overlay, state.overlay),
|
24
|
+
basePos: state.basePos, baseCur: null,
|
25
|
+
overlayPos: state.overlayPos, overlayCur: null
|
26
|
+
};
|
27
|
+
},
|
28
|
+
|
29
|
+
token: function(stream, state) {
|
30
|
+
if (stream.start == state.basePos) {
|
31
|
+
state.baseCur = base.token(stream, state.base);
|
32
|
+
state.basePos = stream.pos;
|
33
|
+
}
|
34
|
+
if (stream.start == state.overlayPos) {
|
35
|
+
stream.pos = stream.start;
|
36
|
+
state.overlayCur = overlay.token(stream, state.overlay);
|
37
|
+
state.overlayPos = stream.pos;
|
38
|
+
}
|
39
|
+
stream.pos = Math.min(state.basePos, state.overlayPos);
|
40
|
+
if (stream.eol()) state.basePos = state.overlayPos = 0;
|
41
|
+
|
42
|
+
if (state.overlayCur == null) return state.baseCur;
|
43
|
+
if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
|
44
|
+
else return state.overlayCur;
|
45
|
+
},
|
46
|
+
|
47
|
+
indent: base.indent && function(state, textAfter) {
|
48
|
+
return base.indent(state.base, textAfter);
|
49
|
+
},
|
50
|
+
electricChars: base.electricChars,
|
51
|
+
|
52
|
+
innerMode: function(state) { return {state: state.base, mode: base}; },
|
53
|
+
|
54
|
+
blankLine: function(state) {
|
55
|
+
if (base.blankLine) base.blankLine(state.base);
|
56
|
+
if (overlay.blankLine) overlay.blankLine(state.overlay);
|
57
|
+
}
|
58
|
+
};
|
59
|
+
};
|
@@ -0,0 +1,131 @@
|
|
1
|
+
// Define search commands. Depends on dialog.js or another
|
2
|
+
// implementation of the openDialog method.
|
3
|
+
|
4
|
+
// Replace works a little oddly -- it will do the replace on the next
|
5
|
+
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
|
6
|
+
// replace by making sure the match is no longer selected when hitting
|
7
|
+
// Ctrl-G.
|
8
|
+
|
9
|
+
(function() {
|
10
|
+
function searchOverlay(query) {
|
11
|
+
if (typeof query == "string") return {token: function(stream) {
|
12
|
+
if (stream.match(query)) return "searching";
|
13
|
+
stream.next();
|
14
|
+
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
15
|
+
}};
|
16
|
+
return {token: function(stream) {
|
17
|
+
if (stream.match(query)) return "searching";
|
18
|
+
while (!stream.eol()) {
|
19
|
+
stream.next();
|
20
|
+
if (stream.match(query, false)) break;
|
21
|
+
}
|
22
|
+
}};
|
23
|
+
}
|
24
|
+
|
25
|
+
function SearchState() {
|
26
|
+
this.posFrom = this.posTo = this.query = null;
|
27
|
+
this.overlay = null;
|
28
|
+
}
|
29
|
+
function getSearchState(cm) {
|
30
|
+
return cm._searchState || (cm._searchState = new SearchState());
|
31
|
+
}
|
32
|
+
function getSearchCursor(cm, query, pos) {
|
33
|
+
// Heuristic: if the query string is all lowercase, do a case insensitive search.
|
34
|
+
return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
|
35
|
+
}
|
36
|
+
function dialog(cm, text, shortText, f) {
|
37
|
+
if (cm.openDialog) cm.openDialog(text, f);
|
38
|
+
else f(prompt(shortText, ""));
|
39
|
+
}
|
40
|
+
function confirmDialog(cm, text, shortText, fs) {
|
41
|
+
if (cm.openConfirm) cm.openConfirm(text, fs);
|
42
|
+
else if (confirm(shortText)) fs[0]();
|
43
|
+
}
|
44
|
+
function parseQuery(query) {
|
45
|
+
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
46
|
+
return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
|
47
|
+
}
|
48
|
+
var queryDialog =
|
49
|
+
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
50
|
+
function doSearch(cm, rev) {
|
51
|
+
var state = getSearchState(cm);
|
52
|
+
if (state.query) return findNext(cm, rev);
|
53
|
+
dialog(cm, queryDialog, "Search for:", function(query) {
|
54
|
+
cm.operation(function() {
|
55
|
+
if (!query || state.query) return;
|
56
|
+
state.query = parseQuery(query);
|
57
|
+
cm.removeOverlay(state.overlay);
|
58
|
+
state.overlay = searchOverlay(query);
|
59
|
+
cm.addOverlay(state.overlay);
|
60
|
+
state.posFrom = state.posTo = cm.getCursor();
|
61
|
+
findNext(cm, rev);
|
62
|
+
});
|
63
|
+
});
|
64
|
+
}
|
65
|
+
function findNext(cm, rev) {cm.operation(function() {
|
66
|
+
var state = getSearchState(cm);
|
67
|
+
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
68
|
+
if (!cursor.find(rev)) {
|
69
|
+
cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
|
70
|
+
if (!cursor.find(rev)) return;
|
71
|
+
}
|
72
|
+
cm.setSelection(cursor.from(), cursor.to());
|
73
|
+
state.posFrom = cursor.from(); state.posTo = cursor.to();
|
74
|
+
});}
|
75
|
+
function clearSearch(cm) {cm.operation(function() {
|
76
|
+
var state = getSearchState(cm);
|
77
|
+
if (!state.query) return;
|
78
|
+
state.query = null;
|
79
|
+
cm.removeOverlay(state.overlay);
|
80
|
+
});}
|
81
|
+
|
82
|
+
var replaceQueryDialog =
|
83
|
+
'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
84
|
+
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
|
85
|
+
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
|
86
|
+
function replace(cm, all) {
|
87
|
+
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
|
88
|
+
if (!query) return;
|
89
|
+
query = parseQuery(query);
|
90
|
+
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
|
91
|
+
if (all) {
|
92
|
+
cm.operation(function() {
|
93
|
+
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
94
|
+
if (typeof query != "string") {
|
95
|
+
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
96
|
+
cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
97
|
+
} else cursor.replace(text);
|
98
|
+
}
|
99
|
+
});
|
100
|
+
} else {
|
101
|
+
clearSearch(cm);
|
102
|
+
var cursor = getSearchCursor(cm, query, cm.getCursor());
|
103
|
+
function advance() {
|
104
|
+
var start = cursor.from(), match;
|
105
|
+
if (!(match = cursor.findNext())) {
|
106
|
+
cursor = getSearchCursor(cm, query);
|
107
|
+
if (!(match = cursor.findNext()) ||
|
108
|
+
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
109
|
+
}
|
110
|
+
cm.setSelection(cursor.from(), cursor.to());
|
111
|
+
confirmDialog(cm, doReplaceConfirm, "Replace?",
|
112
|
+
[function() {doReplace(match);}, advance]);
|
113
|
+
}
|
114
|
+
function doReplace(match) {
|
115
|
+
cursor.replace(typeof query == "string" ? text :
|
116
|
+
text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
117
|
+
advance();
|
118
|
+
}
|
119
|
+
advance();
|
120
|
+
}
|
121
|
+
});
|
122
|
+
});
|
123
|
+
}
|
124
|
+
|
125
|
+
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
|
126
|
+
CodeMirror.commands.findNext = doSearch;
|
127
|
+
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
|
128
|
+
CodeMirror.commands.clearSearch = clearSearch;
|
129
|
+
CodeMirror.commands.replace = replace;
|
130
|
+
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
|
131
|
+
})();
|
@@ -0,0 +1,131 @@
|
|
1
|
+
(function(){
|
2
|
+
function SearchCursor(cm, query, pos, caseFold) {
|
3
|
+
this.atOccurrence = false; this.cm = cm;
|
4
|
+
if (caseFold == null && typeof query == "string") caseFold = false;
|
5
|
+
|
6
|
+
pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
|
7
|
+
this.pos = {from: pos, to: pos};
|
8
|
+
if (!pos) debugger;
|
9
|
+
|
10
|
+
// The matches method is filled in based on the type of query.
|
11
|
+
// It takes a position and a direction, and returns an object
|
12
|
+
// describing the next occurrence of the query, or null if no
|
13
|
+
// more matches were found.
|
14
|
+
if (typeof query != "string") { // Regexp match
|
15
|
+
if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
|
16
|
+
this.matches = function(reverse, pos) {
|
17
|
+
if (reverse) {
|
18
|
+
query.lastIndex = 0;
|
19
|
+
var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
|
20
|
+
while (match) {
|
21
|
+
start += match.index + 1;
|
22
|
+
line = line.slice(start);
|
23
|
+
query.lastIndex = 0;
|
24
|
+
var newmatch = query.exec(line);
|
25
|
+
if (newmatch) match = newmatch;
|
26
|
+
else break;
|
27
|
+
}
|
28
|
+
start--;
|
29
|
+
} else {
|
30
|
+
query.lastIndex = pos.ch;
|
31
|
+
var line = cm.getLine(pos.line), match = query.exec(line),
|
32
|
+
start = match && match.index;
|
33
|
+
}
|
34
|
+
if (match && match[0])
|
35
|
+
return {from: {line: pos.line, ch: start},
|
36
|
+
to: {line: pos.line, ch: start + match[0].length},
|
37
|
+
match: match};
|
38
|
+
};
|
39
|
+
} else { // String query
|
40
|
+
if (caseFold) query = query.toLowerCase();
|
41
|
+
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
|
42
|
+
var target = query.split("\n");
|
43
|
+
// Different methods for single-line and multi-line queries
|
44
|
+
if (target.length == 1) {
|
45
|
+
if (!query.length) {
|
46
|
+
// Empty string would match anything and never progress, so
|
47
|
+
// we define it to match nothing instead.
|
48
|
+
this.matches = function() {};
|
49
|
+
} else {
|
50
|
+
this.matches = function(reverse, pos) {
|
51
|
+
var line = fold(cm.getLine(pos.line)), len = query.length, match;
|
52
|
+
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
53
|
+
: (match = line.indexOf(query, pos.ch)) != -1)
|
54
|
+
return {from: {line: pos.line, ch: match},
|
55
|
+
to: {line: pos.line, ch: match + len}};
|
56
|
+
};
|
57
|
+
}
|
58
|
+
} else {
|
59
|
+
this.matches = function(reverse, pos) {
|
60
|
+
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
|
61
|
+
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
62
|
+
if (reverse ? offsetA >= pos.ch || offsetA != match.length
|
63
|
+
: offsetA <= pos.ch || offsetA != line.length - match.length)
|
64
|
+
return;
|
65
|
+
for (;;) {
|
66
|
+
if (reverse ? !ln : ln == cm.lineCount() - 1) return;
|
67
|
+
line = fold(cm.getLine(ln += reverse ? -1 : 1));
|
68
|
+
match = target[reverse ? --idx : ++idx];
|
69
|
+
if (idx > 0 && idx < target.length - 1) {
|
70
|
+
if (line != match) return;
|
71
|
+
else continue;
|
72
|
+
}
|
73
|
+
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
74
|
+
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
75
|
+
return;
|
76
|
+
var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
|
77
|
+
return {from: reverse ? end : start, to: reverse ? start : end};
|
78
|
+
}
|
79
|
+
};
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
SearchCursor.prototype = {
|
85
|
+
findNext: function() {return this.find(false);},
|
86
|
+
findPrevious: function() {return this.find(true);},
|
87
|
+
|
88
|
+
find: function(reverse) {
|
89
|
+
if (!(reverse ? this.pos.from : this.pos.to)) debugger;
|
90
|
+
var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
|
91
|
+
function savePosAndFail(line) {
|
92
|
+
var pos = {line: line, ch: 0};
|
93
|
+
self.pos = {from: pos, to: pos};
|
94
|
+
self.atOccurrence = false;
|
95
|
+
return false;
|
96
|
+
}
|
97
|
+
|
98
|
+
for (;;) {
|
99
|
+
if (this.pos = this.matches(reverse, pos)) {
|
100
|
+
if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
|
101
|
+
this.atOccurrence = true;
|
102
|
+
return this.pos.match || true;
|
103
|
+
}
|
104
|
+
if (reverse) {
|
105
|
+
if (!pos.line) return savePosAndFail(0);
|
106
|
+
pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
|
107
|
+
}
|
108
|
+
else {
|
109
|
+
var maxLine = this.cm.lineCount();
|
110
|
+
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
|
111
|
+
pos = {line: pos.line+1, ch: 0};
|
112
|
+
}
|
113
|
+
}
|
114
|
+
},
|
115
|
+
|
116
|
+
from: function() {if (this.atOccurrence) return this.pos.from;},
|
117
|
+
to: function() {if (this.atOccurrence) return this.pos.to;},
|
118
|
+
|
119
|
+
replace: function(newText) {
|
120
|
+
if (!this.atOccurrence) return;
|
121
|
+
var lines = CodeMirror.splitLines(newText);
|
122
|
+
this.cm.replaceRange(lines, this.pos.from, this.pos.to);
|
123
|
+
this.pos.to = {line: this.pos.from.line + lines.length - 1,
|
124
|
+
ch: lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)};
|
125
|
+
}
|
126
|
+
};
|
127
|
+
|
128
|
+
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
129
|
+
return new SearchCursor(this, query, pos, caseFold);
|
130
|
+
});
|
131
|
+
})();
|